如何正确地在C#中使用COM组件

跟这篇《如何正确地在C++中使用COM组件是一个系列。

 

在C#中调用COM是很简单的,VS会自动帮你生成一个Interop包装库,这个库里自动生成了COM库的函数的托管定义。

在项目的解决方案资源管理器中找到当前项目,右击“引用”,选择“添加引用”,然后选择COM,找到需要的COM库就OK了。

有时会出现这样的错误提示:

错误 CS1752 无法嵌入互操作类型“CPAdderClass”。请改用适用的接口。

这种情况下,选择引用中的这个包装库,在属性中将“嵌入互操作类型”改为False即可。

 

——全文完——

 

到这里都没什么值得说的。

但是继续谈到前几篇的问题,那就是这个COM库本身有问题的情况。

对于QQ的CPHelper这个COM库,我们传入的字符串(从托管到非托管时自动转为BSTR,BSTR是一种Unicode编码、附有长度信息的类型)被错误地当做了ANSI编码并进行了向UTF8的转码导致了错误。

此时自然会想到,能不能传入ANSI格式的字符串呢?事实上前篇(C++篇)也是在做这种尝试。

我们看一下C#中相关的东西,找到一个MarshalAsAttribute标签,用来定义托管与非托管之间传递数据的格式,这正是我们需要的。

用法:[MarshalAs(UnmanagedType.BStr)]
public virtual extern int PutRSInfo(uint dwUin, uint dwRsID, [MarshalAs(UnmanagedType.BStr)] string strRSPrompt, [MarshalAs(UnmanagedType.BStr)] string strParam);

 

来看一下这个UnmanagedType枚举,里面有很多种非托管的字符串格式。在默认情况下,COM库的字符串都是如上所示的UnmanagedType.BSTR。看一下其他格式,其中有个AnsiBSTR看上去尤其有一试的价值。

但是我们从C#中导入COM库时,已经被自动包装了。如何才能手动地改变这个导入函数的定义,加入MarshalAs标签呢?

我们需要这篇文章:《How to: Create Wrappers Manually》。本文演示了如何通过COM库的TLB定义手动用托管代码定义COM库中的类和函数。其中有一堆标签、GUID之类的需要注意。特别是在函数上都要注意添加MethodImplDispId标签(否则会出现找不到RVA之类的错误)。

这样看起来仍旧麻烦,而且一旦某个地方没有完全按照TLB中的定义来,就有可能跑不起来。我们所需要的只是在自动的包装的基础上稍作修改而已。

这时,从上面那篇文章下面的See Also里,能看到许多有价值的相关内容。我们需要这篇文章:《How to: Edit Interop Assemblies》。——还真是要啥有啥啊。但是点进去一看:This topic is no longer available。然而切换到旧版文档(.NET 4),还是能看的。

仔细一看,步骤很简单:自动生成包装库->反编译成IL->修改IL->重新编译成库。

怪不得现在不可用了。这还用你说啊。

确实要做修改,反编译是很实际的选择。不过不至于反编译到IL。我们可以用反编译工具反编译为C#工程。

这里插播一点别的吐槽:在这里不要用JustDecompile来反编译,出来的代码会漏掉很多标签。推荐使用Reflector。

反编译出来的项目导入到VS,找到对应的函数定义,修改MarshalAs标签(如果你发现本来没有标签,很可能你需要换个工具。不过默认情况会自动设定为BSTR,所以没有标签也不一定是错误的),编译。然后从别的项目中尝试调用,若没有异常就OK了。

通过这一系列过程,成功将PutRSInfo函数的字符串参数修改成AnsiBSTR,结果是:依旧乱码。

果然这个库它就是有问题啊!它接收的确实不是AnsiBSTR而就是BSTR(Unicode)啊!而它确实是把内容又当成了ANSI啊!

最终果然还是只能用前文所述的解决方案了。

特别节目:评.NET反编译器
ILSpy:代码可读性中等偏下,不能反编译出复杂的lambda表达式或是LINQ,经常出现goto。代码可用性较强,通常只需做少量修改。工具本身功能比Reflector略差,不过有不少插件补充,BAML反编译插件和调试器插件是两个最重要的插件。
DnSpy:是从ILSpy的基础上开发出来的。由于反编译部分代码基本一样(注:IL操作库不一样)所以代码的可读性和可用性同ILSpy。但是工具本身加入了很多的操作功能,不只能看还能做一些修改,调试功能更是当前最强。另外界面也比ILSpy好,有中文汉化(超过半数文本是我汉化的,请看Credits),又有新开发的BAML反编译组件等的加入。目前完全可以替代ILSpy。
JustDecompile:只要能找到目标的其它依赖,就能反编译出一部分lambda和LINQ,出现goto的次数是所有同类工具中最少的,可读性很高。但是代码可用性较低,经常会莫名漏掉一些内容。另外搜索功能(曾经)非常……微妙,老是搜不到,与Reflector相比差很远。(Update:现在已经好多了。)所以此工具比较适合用来看某个具体的部分。
Reflector:中规中矩。可读性和可用性都很均衡,比上不足比下有余。关键是此工具的分析、搜索(特别是搜索字符串)和导航功能都很完善,能够快速找到你想要的内容,用起来顺手。此外,此工具支持的语言也是最多的,甚至支持IronPython和C++/CLI。
dotPeek:在属性、LINQ、Lambda、CLI的反编译结果上都是最差的。JetBrains虽然能搞出R#这样的好东西,然而这个工具依然不上心啊。

 

 

添加评论

Loading