公共语言运行库(CLR)开发系列课程(3):COM Interop基础 学习笔记
-
上章地址
-
什么是COM
-
Component Object Model 组建对象模型
-
基于接口(Interface)
- 接口=协议
- IID 标识接口
- V-table 虚表 方式调用
- 单继承
-
对象(Object)
- 实现一个或者多个接口
-
-
举例:IDispatch接口
-
-
为什么要使用COM Interop
- 重用代码:使用.NET调用已有的COM组件,提高生产率:使用.NET编写COM组件
-
COM Interop基础概念
- RCW:Runtime Callable Wrapper .NET可调用COM组件的包装代理 .NET=>RCW=>COM
- CCW:Com Callable Wrapper COM可调用的包装代理 COM=>CCW=>.NET
-
RCW基础
-
定义COM接口
[ComImport] [Guid("....")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] interface IMyInterface { void Func(); }
-
Attributes
- ComImport 接口按照对应的COM接口定义
- Guid 指定IID
- InterfaceType
- ComInterfaceType.InterfaceIsIUnknown 在.NET调用COM组件的时候总是用虚表的方式调用 和C++调用虚函数一样 要知道函数指针以及偏移量
- Early Bound 早绑定
- 基于虚表
- 函数顺序必须和COM接口完全一致 但是如果你不用后面方法只调用前三个 那么只需要前三个方法对应就行了 不建议可能导致顺序婚论
- ComInterfaceType.InterfaceIsIDispatch 通过函数名字 和 Displd调用 COM组件用VB写的建议使用这种方式调用 这种方式中间会有Invoke 会慢点 方便点
- Late Bound 后期绑定
- 支持IDispatch
- 通过Displd 属性指定Dispatch ID 或者自动生成
- 函数顺序不重要
- ComInterfaceType.InterfaceIsDual 既支持虚表调用也支持IDispatch方式调用 上面两种他都支持
- 支持 IUnknown和IDispatch两种方式 他需要 顺序和ID都要对
- ComInterfaceType.InterfaceIsIUnknown 在.NET调用COM组件的时候总是用虚表的方式调用 和C++调用虚函数一样 要知道函数指针以及偏移量
-
-
接口的继承
- 需要重新定义父接口的成员函数 父接口函数放前面 自身接口放后面 按顺序声明
[ComImport] interface IA { void FuncA(); } [ComImport] interface IB:IA { void new FuncA(); void FuncB(); }
- 需要重新定义父接口的成员函数 父接口函数放前面 自身接口放后面 按顺序声明
-
定义RCW
-
[ComImport] public class MyRcw:IMyInterface { [MethodImplAttribute(MethodImplOptions.InternalCall)] public extern void MyFunc(); }
-
ComImport 告诉CLR这个是RCW
-
extern + InternalCall 函数由CLR内部实现 具有ComImport特性的时候必须使用extern和InternalCall 强制约束
-
-
自动生成RCW:TlbImp
-
TlbImp<tlb_name>
-
生成Interop Assembly
- Struct/Union
- Enum
- Interface
- Class
-
直接引用即可
-
VS中Add Reference
-
-
使用RCW
-
创建RCW MyRCW rcw=new MyRCW();
-
释放RCW GC自动处理 AppDomain Unload 时候 Marshal.ReleaseComObject Marshal.FinalReleaseComObject
-
调用方法 rew.Func()
-
Cast
- ImyInterface2 interface2=rcw as IMyInterface2; 可能成功
- 实际上对应QueryInterface调用
- 不需要RCW类型本身实现IMyInterface2!
- 失败则抛出异常(Cast)或者返回Null (as 操作符)
-
-
DEMO
不会建立com 组件的请参考csdn上一位大牛的帖子 上面有详细的创建流程在此感谢大牛的分享 ,这里在分享一篇有心人收集的对应表 com 我个人感觉用的是Windows数据类型 自己参考查看 对应表
c#代码片段
class Program { [ComImport, Guid("31E95758-B52C-4252-B4E0-F33547F9B55A")] public interface IMyATLClass { [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(1)] void Add([In] int para1, [In] int para2); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(2)] void PopupDialog(string text); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(3)] void Sum([In] int para1, [In] int para2, out int Sum); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(4)] void Messg([In] string test); } [ComImport, CoClass(typeof(MyATLClassClass)), Guid("31E95758-B52C-4252-B4E0-F33547F9B55A")] public interface MyATLClass : IMyATLClass { } [ComImport, Guid("D14CE5C7-9648-427B-BEAC-504E1A91DDAE")] public class MyATLClassClass : IMyATLClass, MyATLClass { [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(1)] public virtual extern void Add([In] int para1, [In] int para2); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(2)] public virtual extern void PopupDialog([MarshalAs(UnmanagedType.BStr)] string text); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(3)] public virtual extern void Sum([In] int para1, [In] int para2, out int Sum); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(4)] public virtual extern void Messg([In] string test); } static void Main(string[] args) { // MyATLComLib.IMyATLClass aa = new MyATLComLib.MyATLClass(); //int i; //aa.Sum(1, 1, out i); //aa.Add(1, 1); //IntPtr sb = Marshal.StringToCoTaskMemAnsi("123"); IMyATLClass aa = new MyATLClass(); int i; aa.Sum(1, 1, out i); //aa.Add(1, 1); // IntPtr sb = Marshal.StringToCoTaskMemUni("21"); string a = "a"; IntPtr aPtr = Marshal.StringToHGlobalAnsi(a); IntPtr helloPtr = Marshal.StringToHGlobalAnsi("aaaa"); // aa.PopupDialog("a1"); aa.Messg("哈哈哈哈"); Console.Write(i); Console.Read(); // int i; // DemoObjectLib.IMyCOMDemo aaaa= new DemoObjectLib.MyCOMDemoClass(); } }
c++代码片段
// MyATLClass.cpp : CMyATLClass 的实现 #include "stdafx.h" #include "MyATLClass.h" // CMyATLClass STDMETHODIMP CMyATLClass::Add(LONG para1, LONG para2) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); LONG a = para1 + para2; AfxMessageBox(a); // TODO: 在此添加实现代码 return S_OK; } STDMETHODIMP CMyATLClass::PopupDialog(CHAR* text) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); // TODO: 在此添加实现代码 AfxMessageBox((LPCTSTR)text); return S_OK; } STDMETHODIMP CMyATLClass::Sum(LONG para1, LONG para2, LONG* sum) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); // TODO: 在此添加实现代码 *sum = para1 + para2; return S_OK; } STDMETHODIMP CMyATLClass::Messg(BSTR test) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); // TODO: 在此添加实现代码 AfxMessageBox(test); return S_OK; }