在使用 dynamic
关键字 ( more information here ) 从 C# 调用 COM TLB 方法时,我遇到了一些性能问题。因为我没有任何运气来尝试优化这样的调用,所以我现在正在寻找一种方法将我的 TLB 库转换为我可以直接在我的 C# 项目中使用的 native 包装 DLL(在这个阶段我什至不确定这是否会帮助,而是将性能问题抽象到一层)。
我已经使用 tlbimp.exe
使用 VS2013 命令提示符和命令从我的 COM .tlb 文件(已注册)创建一个 .dll
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.*/ }
}
和两个接口(interface) IGrpCall
包含生成类的完整模板和空接口(interface) GrpCall
。我使用 regasm.exe
和
regasm F:\SomeDir\GrpSvr.dll
现在,我在我的项目中包含对此 .dll 的引用,并尝试通过
实例化GrpCallClass
类
private GRPSVR.GrpCallClass grpSvr = new GRPSVR.GrpCallClass();
但这会导致编译时错误:
Interop type 'GRPSVR.GrpCallClass' cannot be embedded. Use the applicable interface instead.
那我试试
private GRPSVR.IGrpCall grpSvr = new GRPSVR.GrpCall();
这在编译时有效,但在运行时我得到一个
Additional information: Retrieving the COM class factory for component with CLSID {FFB54BC4-B15E-11D1-99BC-0000E803C444} failed due to the following error: 80040154 Class not registered (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG)).
但是我注册了.dll。
这种方法是否有助于提高初始 COM TLB 程序集中调用方法的性能?
有人可以解释我做错了什么以及如何将我的 COM .tlb 库转换为 native 包装器 .dll 吗?
感谢您的宝贵时间。
最佳答案
I register this .dll using regasm.exe
这是一个致命的错误。这覆盖了 native COM 服务器的注册表项。仅当服务器是用 .NET 语言编写时才应使用 Regasm。您将不得不重新安装服务器以修复损坏。请务必先运行 Regasm/unregister 以清理注册表。
and empty interface GrpCall
您看到一个空接口(interface)是您必须在 C# 代码中使用 dynamic 关键字的原因。 COM 服务器的作者只允许您在后期绑定(bind)时使用它。相当于 .NET 中的 [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]。这并不少见,它避免了很多 DLL hell 的痛苦。但是可以肯定的是,后期绑定(bind)不是很快,因为在运行时需要额外的调用来查找您正在调用的方法的 dispid。设置堆栈帧以进行调用也很慢,每个参数都必须转换为 VARIANT。与早期绑定(bind)调用相比,速度降低 10 倍是很正常的,对于一个简单的属性来说可能是几个数量级。
您无法避免这种情况,您必须与服务器作者合作才能取得成功。一个漂亮的顶部奶酪是解决它的方法,要求双界面。期待一个“否”,你可能会得到一个"is"。
关于c# - 从 TLB 程序集创建 DLL,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22711523/