COM裏面,套間是一個 想象中的邊界,用來在多線程環境中安全使用線程安全和線程不安全的COM對象。什麼叫做線程安全的COM對象呢?再多線程環境中,如果這個COM對象自己實現了同步機制,可以被多個線程同時調用而不破壞對象內部數據的完整性的話,那麼這個對象就叫做線程安全的對象。然而COM對象有一個目標就是,即使在多線程環境裏面也可以安全地使用線程不安全的COM對象。也就是說,即使COM對象內部沒有實現同步機制,COM也有一個機制可以創建一個線程安全的環境來使用這個對象,這個機制就是套間。在多線程環境裏面,套間爲線程不安全的COM對象創建了一個同步機制,COM保證在任意時刻都只有一個客戶端在調用線程不安全的COM對象(調用它的函數—COM世界裏面只有函數和接口)。
關於套間的知識,可以參考下面兩篇文章:
http://www.codeguru.com/cpp/com-tech/activex/apts/article.php/c5529
http://www.codeguru.com/cpp/com-tech/activex/apts/article.php/c5533
而如果需要跨套間調用COM對象,這個函數調用,調用使用的參數和函數調用返回值都需要在套間之間被列集。而如果你的參數裏面使用到了COM接口的話,例如跨套間使用一個COM對象,並且調用這個對象的QueryInterface方法,QueryInterface返回的接口就需要被列集。COM庫使用CoMarshalInterThreadInterfaceInStream 和CoGetInterfaceAndReleaseStream來列集接口。
CoMarshalInterThreadInterfaceInStream 查詢註冊表HKEY_CLASSES_ROOT/Interface/{IID}/ProxyStubClsid32中要列集的接口是否註冊有列集程序(Proxy和Stub程序)。
1. 如果這個鍵值存在,CoMarshalInterThreadInterfaceInStream會激活裏面CLSID對應的COM對象來完成接口的列集;
2. 如果沒有這個鍵值,那麼說明沒有提供方法列集接口,因此QueryInterface返回E_NOINTERFACE。
如果你細心一點的話,會發現很多接口的ProxyStubClsid32裏面的CLSID是一樣的,而且這些接口通常都會有另外一個子鍵:TypeLib。這是因爲手工編寫處理接口列集的COM對象的工作繁瑣又容易出錯,
1. 所以對於一些Dual接口,COM庫(實際上是OLEAUT32.dll)提供了一個通用的類來列集所有的Dual接口,它所需要的就是類型庫文件—因爲類型庫裏面包含了所有COM對象的元數據(Meta Data);
2. 另外,對於非Dual接口,你也可以使用MIDL根據IDL文件生成對應的列集接口的COM對象。
這是在上一篇文章裏面例子程序裏面出現InvalidCastException的原因,後一篇我會介紹如何更改.NET代碼修復這個問題。