C++因而C#编写一个经过外之COM组件示例代码讲解

 

代码的链接以《用C#修一个进程外之COM组件》,小技巧:如果你如果同时看示例代码和讲课的话,可以据此浏览器分别打开示例代码和当下首文章,然后运Windows提供的纵向平铺窗口作用就是可同时看片篇稿子了。

 

TestComVisibleClass.cs里面定义了咱若公布为COM客户程序的.NET对象,由于我们的.NET进程外组件需要调用几个COM库的API,因此于ComHelperClass里面定义这些API在.NET里面的声明方式,正确地声称P/Invoke函数的原型非常窘迫,要求程序员对Win32,COM和托管代码都非常熟悉才可以做,所以我写了另外一篇文章《下Signature Tool自动生成P/Invoke调用Windows API的C#函数声明》简化P/Invoke函数声明的步骤。

 

ComHelperClass类里面CoRegisterClassObject函数的原型比较好玩,注意rclsid参数前面的[MarshalAs(UnmanagedType.LPStruct)]性,这个特性告诉.NET,在从.NET一端传递rclsid参数值到非托管代码一端时,不要用默认的列集(Marshal)规则,在P/Invoke里面,NET默认将结构体对象完整复制到非托管的内存里,使用UnmanagedType.LPStruct告诉NET将Guid对象的指针传递给非托管函数,就省了当调用的时段添加ref关键字之辛苦,UnmanagedType.LPStruct会有另外一首文章来分解其,它有点特别。.NET默认将接近实例对象列集成VARIANT拷贝到非托管的外存里,因此次个参数我用[MarshalAs(UnmanagedType.IUnknown)]通知.NET需要用是目标的例列集成IUnknow
*指针。

 

我们的.NET进程外组件有或受一些C++编写的客户端调用到,对于C++程序来说,使用前绑定(即经过接口指针调用接口成员函数)的道会进一步便宜一些,否则用延缓绑定技术(通过IDispatch接口调用接口成员函数)的措施C++代码会比较复杂一些。因此用TestComVisibleClass的有些艺术及性提取变成一个接口,并且分别给ITestComVisible接口和TestComVisibleClass类分配了一个GUID,在COM世界里,前者就是咱熟知的IID,后者则是CLSID。

 

再就是,为了便利VB程序使用.NET进程外组件,我还特地为ITestComVisible接口的性质与函数指定了DispID,因为以OLE规范里,DispID为0,-1等几乎单特殊值的函数有异样之义,这无异碰自己拿会当末端的稿子里讲到。

 

是因为我们无能够就此mscoree.dll自己提供的类激活策略来当COM中激活我们的.NET对象,mscoree.dll默认提供的激活策略也会见当后的篇章里说道到,我们只能显示地提供类厂,并且将我们的类厂在COM运行库中注册一下。类厂(ClassFactory)的连锁接口同样用定义一个C#花样之原型,一个比较取巧的方法就是是利用tlbimp生成一个dll,或者就看看System.Runtime.InteropServices.ComTypes命名空间内是否已经发出定义好了底型?IClassFactory最重大之一个函数就是CreateInstance,我们的贯彻即是探望客户端需要什么体统的接口,如果是IUnknown或者是我们公布之ITestComVisible(102执至107执),否则就当109尽的职务及摒弃来一个可怜(碰到错误就是撇下大的习惯于COM世界里无是一个和谐的艺术,我的代码为了简单就以了弃大的方式,更好的做法是归错误码,由COM客户端决定哪些处理这荒唐。)

 

每当次启动的时候,我们拿团结实现之类厂注册于COM运行库中(Program.cs的48履行至53履行),记得保留CoRegisterClassObject返回给咱们的注册ID(第53实施)。Program.cs里面的35实施至44实践而挑选,它们的目的是举行一些平安检查,确保只来一部分闹权力的用户才能够调用你的C#
Dcom组件,如果您对安全性不关注的话语,可以去除其。在先后退出的早晚(Program.cs的26执行到29实施)扫除一些漏洞,释放部分资源。

 

这就是说怎么咱们还要一个注册表文件呢?这是以在前面绑定调用方式里,我们要用指针在过程中传递,比如在客户端C++代码的第21实践,实际上CoCreateInstanceEx需要启动我们的.NET进程(也就算是经过外COM服务器),调用我们于Program.cs的第53尽注册过了之IClassFactory接口来创建.NET对象实例,然后将实例的ITestComVisible接口指针从.NET进程传回C++客户端进程里来。可能有人会说,可以直接拿指针的地方及C++客户端进程去呗?这是十分的,因为Windows操作系统将经过同其余进程独立开来,简单说,一个经过中的虚拟内存地址以另外一个历程之中或借助为一个废物,原理请参考操作系统书籍内关于虚拟内存的讲述。

 

为以经过之中传递ITestComVisible接口指针,COM库需要明白如何列集ITestComVisible指针,一般情形下,程序员需要提供另外一个DLL,这个DLL包含了列集ITestComVisible指针的代码。为什么要供代码来列集指针的案由是,不同的接口包含不同之函数,例如当客户端C++代码的第25行与第26履,COM库需要列集远程函数调用(RPC),也不怕是索要一个方式以TestMethod和Release的调用区分开来。一般是DLL,可以由此用msidl.exe分析IDL文件来生成列集函数的源代码编译生成。然而,这种办法较费心,因此微软供了OLEAUT32.dll,里面来一个通用的接口列集函数(但是这个函数不能够列集所有的接口,可以列集的接口需要依照一些条条框框,这个后面有工夫重新谈),可以经分析TLB文件来列集指针,因为TLB文件相当于.NET Assembly里面的首先数据,oleaut32.dll可以知晓乃的com组件里面有什么接口,各个接口的扬言又是怎么的,函数的参数类型是什么等等。但是oleaut32.dll需要查询注册表才能够领悟接口存在的Tlb文件之职位:

1.         因此注册表代码里面的第3尽及第16行每当注册表里面保存了类型库的寄放路径,注意,在COM世界里,tlb文件也是为此GUID来唯一标识的,你可以用oleview.exe打开regasm.exe或者tlbexp.exe生成的tlb文件,找到[custom(9903F14C-12CE-4c99-9986-2EE3D7D588A8)…]这就是说同样段落文字来找到Tlb文件之GUID。

2.         注册表代码里面的第18行至第28行,在注册表里面保存了列集ITestComVisible接口的消息,例如ProxyStubClsid指的是使oleaut32.dll提供的通用接口列集函数,并且保留了该函数所动tlb文件之音(第26执行及28实施)。如果注册表里面没列集接口的音讯,CoCreateInstanceEx函数会返回E_NOINTERFACE(不支持这接口)错误—一个被人口稀里糊涂的错误代码。

3.         最后注册表代码里面的第30推行及第41行望全球声明(不好意思,不是炎黄公民从此站起了之那种声明):我们的.NET对象非待经mscoree来以COM端激活,自己可以好激活操作,因此我们删掉了由regasm.exe插入的InprocServer32键值,而是增补加了LocalServer32键值。

 

把,上面注册表代码里面的第3推行及第28推行可据此RegisterTypeLib函数来就,而RegisterTypeLib所需要的ptlib参数可以透过LoadTypeLib函数来将到。