代码改变世界

多线程之间COM结构体传递(C#)

2009-12-03 20:51  ubunoon  阅读(2451)  评论(3编辑  收藏  举报

前言:

      此处只提供一个具体的思路,具体的实现与具体的应用相关。


通常,COM中的多线程比一般程序的多线程要复杂的多,这要归功于微软的STA和MTA模型,虽然此模型的目的是为降低线程之间的混杂程度,其结果却是让线程之间的模型变得更为复杂。


一般在STA的COM对象中,多个线程之间的COM对象数据共享变得非常困难,哪怕即使是传递一个COM对象的指针,也可能会出现意向不到的问题。


在《ATL编程指南》中,有一部分讲到了STA中多个线程传递COM对象的指针的方法,使用的Marshal方式。这种方式在COM接口是比较通用的,也是比较不错的,但是很多时候,CoMarshallnterThreadInterfaceInStream和CoGetInterfaceAndReleaseStream这两个API函数并好方面使用,尤其是在不同语言之间,这个方式,可能更加不方便使用。


当底层运行时库已经是多个线程的时候,而COM只是作为接口封装而被使用的情况下。在底层运行时库有自定义结构体的时候,为了能够支持更多的平台通用性,开发与外部条件(语言)无关的COM组件变得更为重要,这个时候,有两种方案可以采用,一种是采用SAFEARRAY传递数据,这是COM内建支持的一种数据传递方式,这种方式非常方便,但是它使用起来并不直观。另一种方式是使用COM组件作为结构体,用COM属性作为结构体的成员,这种方式,COM对象与结构体一一对应,看起来非常不错,但是在不同的线程之间传递这个COM组件接口对象,也非常容易出现错误。


对于SAFEARRAY的实现,网上已经有一篇又一篇的介绍,再次,不在过多的描述。


对于COM对象(就本身对象的)的实现而言,也是非常简单的。此处仅对不同线程之间传递COM对象的方式做一个介绍。


在COM对象被创建的时候,系统内部可以创建一个全局的接口表,称为Global Interface Table(GIT),微软为我们实现了这个接口表的COM对象,通过IGlobalInterfaceTable来实现访问。当把COM对象放入到GIT中后,在进程内的任何线程都可以访问这个COM对象的指针,从而也就方便了多线程之间的数据调用。关于这一部分的描述,在网上也是一把一把的。


当不同语言之间传递数据的时候,使用IGlobalInterfaceTable的RegisterInterfaceInGlobal接口函数返回的Cookie作为传递数据对象,因为这是一个DWORD类型的数据,在任何语言之间都可以方便传递。当需要获取定义的COM对象的时候,使用这个Cookie作为标识符传递到此次可以访问的某个COM对象中(一般为全局的COM对象),当然该COM中要实现一个以Cookie和GUID为标识符,能够返回一个COM对象,比方说GetObject接口。


使用GetObject返回的对象,在C#中,有一个特殊的地方,那就是该对象是未被Class后缀添加的对象,比方说注册的对象接口名称为IMyCom,那么在C#中导入后,会有MyCom和MyComClass两个内容,前一个是接口,后一个是C#对此的一层封装。需要注意的是GetObject,返回的仅仅是MyCom这个接口(尽管接口对象已经被创建)。要使用这个接口对象,转换为MyComClass需要使用Marshal.CreateWrapperOfType(mycom, typeof(MyComClass));然后你才可以正确用这个传递出来的COM对象。


这个方法,应该说一个比较可行的方法,同时也困扰了我许久。


http://www.cnblogs.com/ubunoon