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

3/18/2009

SxS at runtime Part 1: COM 可不可以不註冊又不要描述在崁入的 Manifest 中

標題是苦惱我們許久的問題, 沒想到隨便 survey 了一下就找到解法, 所以答案當然是 YES,

問題是什麼呢就讓我娓娓道來, 我想有寫 Windows 程式的人大概都知道 DLL Hell 這件事, DLL Hell 形成的原因在於不相容, 舉個例子, MFC 或是 C-Runtime 本身就有很多個版本而且各版本之間是不相容的, 同一個環境中是允許有多個版本同時存在的, 那又會發生以下的兩個問題:

  1. 版本不同但是 DLL 的檔案名稱一樣, 例如 MFC 9.0 跟 MFC 9.01 的檔案可能都叫 mfc90.dll
  2. 如果將這些檔案都放在不同的目錄, 上述的問題可以解決, 但是 AP 並不知道要載入哪個檔案才正確

因為這些因素, 微軟在 Windows XP 導入了 Side-by-Side 的概念去解決這個問題, 方法是每個 AP 都有自己的 metadata 來描述載入 DLL 的版本資訊或是方法. 而 Loader 會根據這樣的 metadata 做到路徑的重新導向來達成正確的載入, 該 metadata 稱為 Manifest.

COM 在這方面也有相同的問題, 雖然設計 COM 的初衷是希望每個 interface 的 原型, IOPE 跟 semantics 都是固定的, 這樣自然沒有衝突的問題, 但是這個目標並不容易做到, 現實上也不可能做到, 因為 IDL 只有定義 interface 的原型, 對於 IOPE 與 semantics 則無法規範. 所以 COM 也有 SxS 這樣的概念.

平常我們使用 COM SxS 都必須將該 COM 所包含 Class Object 的 CLSID, threading model, IID, proxy info 寫在 Manifest 中. 而 Manifest 則會被嵌在 DLL 或是 EXE 中. 這也意味著這些資訊是在Building time 就必須決定.

如果我們要支援以下的 case, 就會用一些非正規的手法去達成: 某 AP 允許有 Plug-in 的 module 掛到 AP 中來使用, 而 Plug-in 的 object 是使用 COM 的方式來達成, 而該 Plug-in module 又因為相容性或是部屬方便為考量不願意將 COM 註冊到系統中.

一個比較惡劣的解法是: AP side 使用以下手法 (我也用過啦 不得已 要生活 :( )

  1. LoadLibrary/CoLoadLibrary的方式把 Plug-in module 給 load 進來,
  2. 然後用 GetProcAddress 的方式取得該 module DllGetClassObject 函數的位址,
  3. 呼叫 DllGetClassObject 取得 ClassObject,
  4. AP 呼叫 IClassFactory::CreateInstance 來建立 COM object.

以上聽起來都很完美, 但是有一個致命性的問題, 因為 AP 沒有任何關於該 Plug-in COM module 相關的描述, 所以也無從得知該 COM 的 threading model, 即使知道該物件的 threading model 也無法在需要時建立出適當的 proxy/stub. 然而 CoCreateInstance 可是會根據註冊的資訊與當下 thread 的 threading model 自動判斷與建立對應的 proxy/stub, 簡單的說 該方法讓 COM 的 threading model 崩潰了, 在 thread-safety 方面會產生不少問題, 更大的問題是該方法不會有任何的警告或錯誤, 所以大多數的 programming 都不知道這樣是會有問題.

正確的方法當然就是我的主題了, 使用 Activation Context API 這樣的正規守法,不過這個好東西似乎鮮少人知道, 連 Codeproject 都沒有人在分享, 我也是仔細的看了 MSDN 之後才知道, 附帶一提 Manifest 機制就是使用 Activation Context API 去實做的喔. 詳細的描述請看下篇, 或是自己參考一下的 reference 喔. :)

How to Use the Activation Context API, Windows Side-by-Side, Using the Activation Context API, Activation Context Reference