从 TLB 程序集创建 DLL

Moo*_*ght 2 c# com dll interop

我在使用dynamic关键字从 C# 调用 COM TLB 方法时遇到了一些性能问题(更多信息在这里)。因为我没有任何运气尝试优化这样的调用,我现在正在寻找一种方法将我的 TLB 库转换为本地包装器 DLL,我可以直接在我的 C# 项目中使用它(在这个阶段我什至不确定这会帮助,而是将性能问题抽象到一层)。

我曾经使用tlbimp.exeVS2013 命令提示符和命令从我的 COM .tlb 文件(已注册)创建一个 .dll

tlbimp F:\SomeDir\GrpSvr.tlb /out:F\SomeDir\GrpSvr.dll 
Run Code Online (Sandbox Code Playgroud)

这产生了一个托管包装器 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.*/ }
}
Run Code Online (Sandbox Code Playgroud)

以及IGrpCall包含生成类和空接口的完整模板的两个接口GrpCall。我使用regasm.exe和注册这个.dll

regasm F:\SomeDir\GrpSvr.dll 
Run Code Online (Sandbox Code Playgroud)

现在,我在我的项目中包含对这个 .dll 的引用,并尝试GrpCallClass通过

private GRPSVR.GrpCallClass grpSvr = new GRPSVR.GrpCallClass();
Run Code Online (Sandbox Code Playgroud)

但这会导致编译时错误:

无法嵌入互操作类型“GRPSVR.GrpCallClass”。请改用适用的接口。

那我试试

private GRPSVR.IGrpCall grpSvr = new GRPSVR.GrpCall();
Run Code Online (Sandbox Code Playgroud)

这在编译时有效,但在运行时我得到一个

附加信息:由于以下错误,检索具有 CLSID {FFB54BC4-B15E-11D1-99BC-0000E803C444} 的组件的 COM 类工厂失败:80040154 类未注册(来自 HRESULT 的异常:0x80040154 (REGDBREGTREG)_CLS)。

但是我已经注册了.dll。

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

  2. 有人可以解释我做错了什么以及如何将我的 COM .tlb 库转换为本地包装器 .dll 吗?

谢谢你的时间。

Han*_*ant 5

我使用 regasm.exe 注册了这个 .dll

那是一个致命的错误。这会覆盖本地 COM 服务器的注册表项。只有当服务器是用 .NET 语言编写时才应该使用 Regasm。您必须重新安装服务器才能修复损坏。一定要先运行 Regasm /unregister 来清理注册表。

和空接口 GrpCall

您看到一个空接口是您必须在 C# 代码中使用dynamic关键字的原因。COM 服务器的作者只允许您后期绑定使用它。相当于 .NET 中的 [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]。这并不少见,它避免了很多 DLL 地狱的痛苦。但是可以肯定的是,后期绑定并不是很快,因为在运行时需要额外的调用来查找您正在调用的方法的 dispid。设置堆栈帧以进行调用也很慢,每个参数都必须转换为 VARIANT。与早期绑定调用相比,x10 的减速是很正常的,对于一个简单的属性来说可能是几个数量级。

您无法采取任何措施来避免这种情况,您必须与服务器作者合作才能取得成功。一个非常讨人喜欢的奶酪是解决问题的方法,要求双界面。期待一个“不”,你可能会得到一个“是”。