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代码修复这个问题。