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

3/20/2009

SxS at runtime Part 2: 動態載入 Manifest 來改變 SxS 的組態

SxS 可說是種 context 用來描述 process 當下對於 DLL 載入的組態方式 所以每個 process 可以擁有自己的 SxS 組態, 這個組態稱做 Activation Context, 以 DLL 本身來說, SxS 說明了為相依的 DLL 描述了相關的版本跟 binding (WOW 用語 綁定) 的方式, 而 COM 則描述了 Class Object 的 binding 方式, 概念上比較像是建立一個 process 自己獨有的 registry database view (就像是環境變數的overwrite), 因為 CoCreateInstance 是利用 registry database 來找尋實做指定 CLSID 的 DLL 並且 Activate (建立Class Object 跟 COM object). 當然 Class Window 也是 SxS 的對象之一, 詳細可以參考 MSDN Creating Side-By-Side Windows Classes. 大部分的人都知道利用 process 的外部或是嵌入的 Manifest 來控制 process 建立時的 activation context, 這個方法可以外在的編修 Manifest 在 process 執行前完成, 好處是不用寫任何的程式碼就可以設定 activation context, 而壞處則是無法再執行時期改變組態, 在上一篇的文章我就提到一種使用的情境必須在執行時期改變 activation context, 因為調用的 COM module 並不希望被註冊也希望可以是以 plug-in 的方式掛進我們的程式. Windows API 提供了執行時期改變 activation context 的方法, 稱為 Activation Context API. Activation context 的概念比較像是個 stack 並且每個 thread 都可以有自己的 context stack (請參考 MSDN), 當我們利用 API 建立新的 context 時, 它會覆蓋掉原本的 context, 直到我們釋放我們所建立的 context. 第一個 context 由系統提供, loader 在執行 process 的第一個 thread 時會利用預設的 Manifest 來 overwrite 系統的 context. 而每一次 CreateThread 時, 建立的 new thread 都會繼承當下的 Activation context. 值得一提的是 Activation context 由於是 pre-thread 的, 所以我們必須考慮多個特定的狀況
  1. 當我們使用 SendMessage 或是 PostMessage 給別的 thread 時, Activation context 會怎麼樣? 我們知道當 message 傳遞跨不同的 thread 時, 執行 message 的 thread 將會與發送的 thread 不同.
  2. 當我們在一個不相容於 COM threading model 的 thread 建立 COM object, 系統會幫我們建立對應的 host thread (希望大家都知道這件事), 而這個 host thread 的 Activation context 會怎麼樣?
  3. COM 的 host thread 可能被多個 COM objects 所 host, 而這些建立 COM objects 的每一個 thread 可能都有不同的 Activation context, 那又會如何呢?

第 1 個問題, MSDN 上面的描述是: Windows API 會特別處理 SendMessage 或是 PostMessage 等.... 多個跟 Message queue 有關的 API, 當 Message 被傳遞時 Activation context 也會跟著傳遞, 來防止 context 衝突產生的問題, 不過沒有詳細的描述 Activation context 是何時被 push 何時被 pop, 不過我猜想是當接收 message 的 thread 呼叫 DispatchMessage 時, thread 的 Activation context 才會被 overwrite, 當 DispatchMessage 返回時則回覆 thread 原本的 context.

第 2 個問題, MSDN 上面的描述是: CoCreateInstance 會自動處理 host thread 的 Activation context, 當 host thread 在乎較 LoadLibrary 或是 DllGetClassObject (之前已經被 load 進來了) 之前, 會切換 context 到 creating thread 的 Activation context.

第 3 個問題就很簡單可以回答了, 因為 host thread 是以 Message queue 的方式在接收與執行 COM client 對 COM server 的函數呼叫, 而 proxy 在產生函數呼叫是以 SendMessage 的方式傳遞給 stub, 所以 Activation context 自然的就會被轉移了.

以下是貼來的程式碼 (How to Use the Activation Context API), 說明如何使用 Activation context API, 連結中還有 C# 的使用方式:

ACTCTX actctx = {sizeof(actctx)}; actctx.lpSource = L"Lifetime.Example.Manifest"; HANDLE hActCtx = CreateActCtx(&actctx); ULONG_PTR cookie = 0; BOOL fOK = ActivateActCtx(hActCtx, &cookie); sampleDLL::IsxsSampleControl *pControl = NULL; HRESULT hr = CoCreateInstance(__uuidof(sampleDLL::CsxsSampleControl), NULL, CLSCTX_INPROC_SERVER, __uuidof(sampleDLL::IsxsSampleControl), (void**)&pControl); DeactivateActCtx(0, cookie); ReleaseActCtx(hActCtx);
How to Use the Activation Context API, Windows Side-by-Side, Using the Activation Context API, Activation Context Reference