现在的位置: 首页 > 综合 > 正文

COM线程模型(六)

2013年10月12日 ⁄ 综合 ⁄ 共 1659字 ⁄ 字号 评论关闭

当是进程外组件时,由主函数调用CoInitializeEx或CoInitialize指定组件所在套间,与上面的相同,CoInitialize代表STA,CoInitializeEx( NULL, COINIT_MULTITHREADED );代表MTA,没有NA。因为NA是COM+提供的,而COM+服务只能提供给进程内服务器,因此只使用上面的注册表项的规则决定DLL组件是否放进NA套间,而没有提供类似CoInitializeEx(
NULL, COINIT_NEUTRAL );来处理EXE组件。而且如果可以使用CoInitializeEx( NULL, COINIT_NEUTRAL );将导致调用线程和NA套间相关联了,违背了NA的线程模型,这也是为什么ThreadingModel键在<CLSID>\InprocServer32键下。


跨套间调用

STA线程1创建了一个STA对象,得到接口指针IABCD*,接着它发起STA线程2,并且将IABCD*作为线程参数传入。在线程2中,调用IABCD::Abc()方法,成功或者失败天注定。由于线程2所在的STA套间不同于线程1所在的STA套间,这样线程2就跨套间调用另一个套间的对象了。按照前述的STA规则,IABCD::Abc()应该被转成消息来发送,而如果如上面做法,可以,编译通过,不过运行就不保证了。

COM之所以能够实现前面所说的那些规则(STA、MTA、NA),是因为跨套间调用时,被调用的对象指针是指向一个代理对象,不是组件对象本身。而那个代理对象实现前述的那三个实现算法(转成消息发送,线程切换等),而一般所说的代理/占位对象(Proxy/Stub)等其实都只是指进行汇集工作的代码(后述)。而按照上面直接通过线程参数传入的指针是直接指向对象的,所以将不能实现STA规则,为此COM提供了如下两个函数(还有其他方式,如通过全局接口表GIT)来方便产生代理:CoMarshalInterface和CoUnmarshalInterface(如果在同一进程内的线程间传递接口指针,则可以通过这两个函数来进一步简化代码的编写:CoMarshalInterThreadInterfaceInStream和CoGetInterfaceAndReleaseStream)。
现在重写上面代码,线程1得到IABCD*后,调用CoMarshalInterface得到一个IStream*,然后将IStream*传入线程2,在线程2中,调用CoUnmarshalInterface得到IABCD*,现在这个IABCD*就是指向代理对象的,而不是组件对象了。

因此,前面所说过的所有线程模型的算法都是通过代理对象实现的。要跨套间时,使用CoMarshalInterface将代理对象的CLSID和其与组件对象建立联系的一些必要信息(如组件对象的接口指针)列集(Marshaling)到一个IStream*中,再通过任何线程间通信手段(如全局变量等)将IStream*传到要使用的线程中,再用CoUnmarshalInterface散集(Unmarshaling)出接口以获得指向代理对象的接口指针。因此之所以要获得代理对象的指针是因为想使用COM提供的线程模型(但在COM+中,这不是唯一的理由),如果不想使用大可不必这么麻烦(不过后果自负),并没有强制要求必须那么做。

当线程1和线程2都是MTA时,则可以像最开始说的那样,直接传递IABCD*到线程2中,因为MTA线程模型同意多个线程同时直接调用对象,线程1和线程2在同一个MTA套间中,而那个对象通过某种形式(如ThreadingModel = Free)向COM声明了自己支持MTA线程模型。

而当a.exe的线程1和b.exe的线程2都是MTA时,则依旧需要像上面那样进行接口指针的汇集(列集→传输→散集这个过程)以得到指向代理而非对象的指针,即使线程1和线程2都是在MTA套间中,却是在两个不同的MTA套间中,因此是跨套间调用,需要汇集操作。

抱歉!评论已关闭.