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接口,而不是一个其它什么类型,例如结构什么的.
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接口,而不是一个其它什么类型,例如结构什么的.
相关文章推荐
- C#中COM操作(二)---接口查询
- C#中COM操作(二)---接口查询
- C#与excel互操作的错误无法将类型为“Excel.ApplicationClass”的COM 对象强制转换为接口类型“Excel._Application”
- C#引用COM对象,报错:《类型 *** 未定义构造函数, 无法嵌入互操作类型 *** 。请改用适用的接口》的解决办法。
- C#操作消息队列的代码开发者在线 Builder.com.cn 更新时间:2008-07-20作者: 来源:
- C# 无法嵌入互操作类型shell32.ShellClass,请改用适合的接口
- 使用C#操作Access参数形式的模糊查询方法
- C# 对sharepoint 列表的一些基本操作,包括添加/删除/查询/上传文件给sharepoint list添加数据
- TlbImp生成com的c#用互操作dll
- C#与excel互操作的错误无法将类型为“Microsoft.Office.Interop.Excel.ApplicationClass”的 COM 对象强制
- C#access数据库查询操作(二)
- 有效的使用和设计COM智能指针——条款26:自动查询接口带来方便同时也潜藏危机
- C#操作Word导致 “Microsoft.Office.Interop.Word.ApplicationClass”的 COM 组件错误的解决方法
- 【Hibernate】(2)Hibernate的核心接口、增删改查操作、主键ID生成方式、查询方式
- asp.net(C#)调用C++程序并交互操作【转】http://www.cnblogs.com/greatverve/archive/2010/06/07/Csharp-transfer-Cpp.html
- C#使用XmlDocument操作XML进行查询、增加、修改、删除、保存应用的实例
- mybatis逆向工程的Example类用法==笔记==【单表操作只需调用,多表查询需要自定义sql+mapper接口方法(待补全)】
- C#利用com操作excel释放进程的解决方法
- C#_IO操作_查询指定文件夹下的每个子文件夹占空间的大小
- Atitit. 注册表操作查询 修改 api与工具总结 java c# php js python 病毒木马的原理