My friends, my life, my style - James S.F. Hsieh

6/21/2009

COM Exe: CoRegisterClassObject 的奧秘 1

根據 MSDN 的描述 CoRegisterClassObject 是用來註冊 COM exe 的 class object, 當 COM server 呼叫了 CoRegisterClassObject 之後, 便把一個 class object 註冊在系統中, 系統會根據 CoRegisterClassObject 的引數 rclsid 來得知 class object 的 CLSID, 並且將該 GUID 與 class object 繫結在一起, 當某個 COM client 呼叫了 CoCreateInstance 來建立該物件時, class object 對應的 CreateInstance 函數就會被呼叫並且傳回指定建立的 COM object.
當中有些有趣的行為值得好好探個究竟, CoRegisterClassObject 本身到底做了什麼呢? 以標準的 ATL 建立COM exe 為例子, 我們可以看到呼叫完 CoRegisterClassObject 之後, Process 當中多建立了兩條新的 Thread, 然後 Main thread 就進入 message loop 等待訊息了...
以下是兩個 Thread 的 call stack, 當中最有趣的是其中一個 thread 由rpcrt4.dll 呼叫了 GetQueuedCompletionStatus 這個函數, 該函數是用來等待 I/O completion packet, 而 I/O Completion Ports 是一種較低階的 IPC 機制, 該機制可以建立在 File handle, Winsock2 sockets 或 named pipes 之上, 而 RPC 則又建立在 I/O Completion Port 之上. 有關該機制詳細可以參考 Inside I/O Completion Ports
CoRegisterClassObject 跟 CompletionPort 的關係
RPC 跟 CompletionPort 的關係
以 Apartment COM 為例, 在 COM exe 呼叫CoRegisterClassObject 時, OLE32.dll 建立了一個看不見的 Window 在 Main thread 來建立 Window message queue 以便分派 COM client 的呼叫, 並且利用RPC 來建立跨行程的通道. 當某個 COM client 呼叫了 class object 或是 COM object 的函數時, RPC Thread 會收到對應的 I/O completion packet, 然後再由 Window message 的方式將訊息傳遞給 Main thread, 然後, 再由 ole32.dll 註冊的 WndProcUnmarshaling COM function call的引數並且還原出function call 的 call stack 來達成跨行程的呼叫.