您的位置:首页 > 编程语言 > C#

C#中COM操作(二)---接口查询

2008-08-01 12:22 399 查看
上一篇末留下的一个疑问这一回来作个解答吧。大家看了下面的图就清楚了:

2

3[ComImport, Guid("00000000-0000-0000-C000-000000000046")]

4public interface IUnknown

5

11[ComImport, CoClass(typeof(JetEngineClass)), Guid("9F63D980-FF25-11D1-BB6F-00C04FAE22DA")]

12public interface IJetEngine1 : IUnknown

13

21[ComImport, CoClass(typeof(JetEngineClass)), Guid("9F63D980-FF25-11D1-BB6F-00C04FAE22DA")]

22public interface IJetEngine2

23

35IJetEngine1 iJetEngine = GetJetEngine() as IJetEngine1;

36IntPtr p1;

37iJetEngine.QueryInterface(typeof(IUnknown).Guid, out p1);

38

39IJetEngine2 iJetEngine = GetJetEngine() as IJetEngine2;

40IntPtr p2;

41iJetEngine.QueryInterface(typeof(IUnknown).Guid, out p2);

上面两种方式都是正确的,需要注意的是如果把IUnknown的方法放到IJetEngine2接口内部声明的话,必须放到函数声明的最开始位置,想想虚函数编译后函数指针的顺序就明白了.不过这种方式有个不太好的地方就是搞了老半天好不容易才得到的一个对COM对象的包装类,经过这么一查询接口,又回到了指针形态,很是不爽.

这里说了几种COM接口查询的方式,无非就是COM对象的.NET包装类的引用或者IntPtr指针转来转去的,这两种COM对象的引用到底哪种更好点呢.我的建议是能用包装类引用的尽量用包装类引用吧,实在不济的时候没有声明包装类也可以用object作为引用类型.

我是不太喜欢直接操作COM对象的IntPtr指针的(非它类型的IntPtr指针除外,例如一个指向内存数据块的指针),除非是实在没有办法的时候.原因嘛,就是因为COM引用计数器的问题.前面我们也提到过了,使用COM包装类的引用的时候,不管在接口之间怎么转换,都不会产生新的对象;还有一点就是COM对象的引用计数只会在生成包装类的实例的时候才会增加1;另外COM包装类也是一个托管类,只不过是一个比较特殊的托管类而以,所以它的实例的生命周期还是遵循了一般托管类的生命周期的定义----当该对象没有被任何一个变量所引用的时候,这个对象就需要被垃圾回收了.结合以上几条,一个COM对象只被包装类的实例引用时,在整个包装类的生命周期内,COM的引用计数都只是1,直到包装类被垃圾回收了,这个时候CLR会自动减少这个包装类所指向的COM对象的引用计数,当计数器为0时COM对象也就被销毁了.这个比C++里面的ComPtr还要妙,ComPtr在每赋值一次的时候还要对引用计数加1呢.

回过头来我们再看看使用IntPtr的情况,正如前面所说的,理论上来讲每赋值一次IntPtr都需要对COM计数加1,每当一个有效的IntPtr不再使用了又要对其所引用的COM对象的计数器减1,对于现在C#程序员来说,很多甚至对内存的动态分配和释放都没有概念,更是会经常还要忘了COM计数器的这些操作,编程的乐趣就这样被消磨得没有了,何其痛苦呀.

另外就是在使用自己定义COM包装类和接口的时候,经常会遇到一个接口的方法里面用到了另外的接口,如果一层一层展开下去会需要声明一大堆的接口定义,而我们其实中是需要其中的一个很少的功能,这样太得不尝失了.最简单的方法就从我们的需要出发,保留我们需要调用的方法的接口的声明,其它不相干的接口的参数用object类型或IntPtr定义,在用object作为参数类型的时候需要在参数上加上MarshalAs(UnmanagedType.Interface)特性,以表明这是一个COM接口,而不是一个其它什么类型,例如结构什么的.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐