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

6/29/2009

ATL 方便的幾個小東西

Marshaling

AtlFreeMarshalStream Releases the marshal data and the IStream pointer. AtlMarshalPtrInProc Creates a new stream object and marshals the specified interface pointer. AtlUnmarshalPtr Converts a stream's marshaling data into an interface pointer. CComGITPtr Class This class provides methods for dealing with interface pointers and the global interface table (GIT).

Connection point

AtlAdvise Creates a connection between an object's connection point and a client's sink.
AtlUnadvise Terminates the connection established through AtlAdvise.
AtlAdviseSinkMap Call this function to advise or unadvise all entries in the object's sink event map.
CComPtrBase::Advise Call this method to create a connection between the CComPtrBase's connection point and a client's sink.
Debugging and Error Reporting

AtlHresultFromLastError Returns a GetLastError error code in the form of an HRESULT. AtlHresultFromWin32 Converts a Win32 error code into an HRESULT.

6/27/2009

COM Exe: CoRegisterClassObject 的奧秘 2

在設計 COM exe (out-of-process COM) 的時候, ATL 提供了一個 #define _ATL_FREE_THREADED 或是 #define _ATL_APARTMENT_THREADED 來描述目前建立的 COM exe 是使用哪種 Threading model, 如果選擇 _ATL_APARTMENT_THREADED, 所有 COM exe 所建立出來的 COM object 都會由 Main thread 的 message loop 所推動, 這樣導致所有的 Client 對 COM object 的所有函數呼叫都會被 enqueue 到 message queue, 而 message loop 會將每個呼叫依序的執行, 這樣可以輕易的達到 thread safety.
如果使用 _ATL_FREE_THREADED 將會如何呢? 所有的呼叫都不會透由 message queue 跟 main thread 來執行, 而是直接由 RPC thread pool 來執行, 所以不同的函數呼叫將有機會並行, 很明顯的整個 COM exe 必須自己去處理同步問題.
為什麼這兩個使用這兩個不同的定義有著明顯的差別, OLE32.dll 會知道現在這個 COM object 是用哪種 model 在運行呢, 我 trace 了 ATL 一會終於找玄妙之處, 我們知道 in-process COM 的 Threading model 是被描述在 registry 或是 manifest 之中, 而 CoCreateInstance 會根據 COM Object (Signle, Apartment, Both, Free, ...) 和 thread (STA, MTA) 的 model 來決定傳回的物件是否是 proxy 或是 native pointer, 但是, COM exe 並不一樣, 因為跨行程的關係所有物件都必然會建立 proxy, 而且真正呼叫 COM object 的 thread 是在 COM exe 當中而非 client 的 caller thread, 所以, 而且 COM exe 的 registry 根本不會紀錄 Threading model, 也不可能有manifest 描述, 因為 COM exe 無法 SxS.
所以 Threading 的問題是由COM exe 執行時期來決定的, 原本我猜想這個提示 OLE32.dll 的方式可能設計在 CoRegisterClassObject 的引數, 不過實際一看並沒有這樣的設計, 那 OLE32.dll 到底是怎麼知道的呢? 答案其實很簡單, 就是根據註冊 Class object 的 thread 的 model 來決定的, 簡單說, 如果一個 thread 在呼叫 CoInitializeEx 使用了 COINIT_MULTITHREADED 引數, 則該 Thread 利用 CoRegisterClassObject 所註冊的 COM Object 就會是 Free model, 反之 COINIT_APARTMENTTHREADED 就會是 Apartment model. 所以我們會看到在 ATL 中的 CAtlExeModuleT::InitializeCom 有以下的程式碼:
static HRESULT InitializeCom() throw()
{
#if ((_WIN32_WINNT >= 0x0400 ) || defined(_WIN32_DCOM)) & defined(_ATL_FREE_THREADED)
return CoInitializeEx(NULL, COINIT_MULTITHREADED);
#else
return CoInitialize(NULL);
#endif
}
如此一來, 如果在 COM exe 當中, 所建立 COM object 有 cross-apartment 的方式混合使用其他的 in-process COM , 該結果才不會破壞 Threading model apartment 的邊界. 有趣吧~~

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 來達成跨行程的呼叫.

6/02/2009

2009 05 23 松羅湖登山 + 環島 1209.32km (2)





2009 05 23 松羅湖登山 + 環島 1209.32km (1)