从TLB程序集创建DLL

3

我在使用dynamic关键字从C#调用COM TLB方法时遇到了一些性能问题(更多信息请参考此处)。由于我没有尝试优化这样的调用,所以现在我正在寻找一种将我的TLB库转换为本地包装DLL的方法,以便在我的C#项目中直接使用它(在此阶段,我甚至不确定这是否有帮助,但是可以将性能问题抽象成下一层)。

我使用tlbimp.exe从我的COM.tlb文件(已注册)创建了一个.dll,使用VS2013命令提示符和以下命令:

tlbimp F:\SomeDir\GrpSvr.tlb /out:F\SomeDir\GrpSvr.dll 

这产生了一个托管包装的C# .dll,我可以使用dotPeek进行检查。它包含了预期的命名空间。

namespace GRPSVR
{
    [TypeLibType(TypeLibTypeFlags.FCanCreate)]
    [Guid("FFB54BC4-B15E-11D1-99BC-0000E803C444")]
    [ClassInterface(ClassInterfaceType.None)]
    [ComImport]
    public class GrpCallClass : IGrpCall, GrpCall { /*Expected Methods et al.*/ }
}

这里有两个接口,IGrpCall 包含生成类的完整模板,而另一个空接口 GrpCall。我使用 regasm.exe 注册这个 .dll 文件。

regasm F:\SomeDir\GrpSvr.dll 

现在,我将这个.dll文件的引用包含在我的项目中,并尝试通过以下方式实例化GrpCallClass类:

private GRPSVR.GrpCallClass grpSvr = new GRPSVR.GrpCallClass();

但是这样会产生编译时错误:

Interop 类型 'GRPSVR.GrpCallClass' 无法嵌入。请改用适当的接口。

然后我尝试:

private GRPSVR.IGrpCall grpSvr = new GRPSVR.GrpCall();

在编译时,一切正常,但在运行时我遇到了一个问题:

附加信息:检索 CLSID 为 {FFB54BC4-B15E-11D1-99BC-0000E803C444} 的组件的 COM 类工厂失败,原因是发生以下错误:80040154 类未注册(来自 HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG) 的异常)。

但是我已经注册了 .dll 文件。

  1. 这种方法是否有助于提高调用初始 COM TLB 组件中方法的性能?

  2. 有人能解释一下我的错误在哪里以及如何将我的 COM .tlb 库转换为本地包装器 .dll 吗?

感谢您的时间。


看起来你正在正确的方向上前进。在注册表中查找你的类并检查它是否存在,就像你为任何其他COM对象所做的一样。此外,请注意许多COM组件仅支持32位,因此你的调用应用程序也应该是32位的。请注意,注册工具有32位和64位版本。 - tcarvin
1个回答

5
我使用regasm.exe注册了这个.dll文件,这是一个致命的错误。这覆盖了本地COM服务器的注册表键。当服务器是用.NET语言编写时,才应该使用Regasm。您需要重新安装服务器以修复损坏。一定要先运行Regasm /unregister来清理注册表。
你看到一个空接口是因为COM服务器的作者只允许您在代码中使用dynamic关键字进行后期绑定。这相当于.NET中的[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]。这并不罕见,它避免了很多DLL Hell的痛苦。但是,后期绑定并不是很快,因为运行时需要额外的调用来查找您正在调用的方法的dispid。设置堆栈帧以进行调用也很慢,每个参数都必须转换为VARIANT。与早期绑定调用相比,x10倍的减速非常正常,对于简单属性可能会有几个数量级的差距。
您无法避免这种情况,您将不得不与服务器作者合作解决问题。向他们提出请求,请求双重接口。期望会得到拒绝,但也有可能会被同意。

Hans,再次感谢您的时间。一如既往,非常感激...祝一切顺利。 - MoonKnight
只是想澄清一下,您的意思是即使我从“tlbimp”获取C#包装器,我仍然会有这种性能损失吗?我以为使用这个包装器dll可以避免晚期绑定?我刚刚意识到,在我的VS2013项目中添加对COM库的引用,可以直接访问上面的类/接口 - 这是否又是一种通过后台进行晚期绑定的方法/在这种情况下我的调用会发生什么? - MoonKnight
Ps. 我删掉了生成的 DLL 却没有注销。我太蠢了。现在怎么办才能注销那个 DLL?我尝试重新安装使用 TLB/COM 库的应用程序,但现在无法正确注册!我真是个傻瓜。 - MoonKnight
1
你可以重新运行tlbimp.exe并在dll上使用Regasm /uninstall来清理注册表。使用interop库不会有任何区别,你仍然必须进行后期绑定调用。服务器作者不希望你使用它,所以你不应该使用。这很危险,因为他可能会更改GrpCallClass的[Guid](当他进行任何更改时应该这样做),这将导致你的代码出现“类未注册”的错误。此外,你会期望得到一个“否”的答复。 - Hans Passant
抱歉一直问问题,汉斯。我认识服务器的作者,但这是旧代码。尽管如此,他应该能够进行任何必要的更改,以使其在不使用后期绑定调用的情况下正常工作。我现在很困惑,他如何编译旧代码以实现类型的编译时解析? - MoonKnight
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接