成功注册了COM DLL,但不能使用类方法

我使用regasm.exe注册了一个COM DLL,现在我正在尝试编写一个使用DLL中的类的VBA脚本。 该DLL是ExcelDataReaderLibrary.dll 。 在C#源代码中,该类描述如下(包括来自此库的代码):

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; using System.Data; using Excel; namespace ExcelDataReaderLibrary { public class ExcelDataReader { public void readSheet(string filePath,string sheetName,string outPath) { // code for method here } } } 

我的assembly.cs文件包括以下内容:

 [assembly: ComVisible(true)] [assembly: Guid("b1e78f8f-9ab0-46d8-beac-b843656aacdb")] 

当我打开VBA编辑器并转到引用时,我看到ExcelDataReaderLibrary的引用。 请注意,与此引用关联的文件是ExcelDataReaderLibrary.tlb ,而不是ExcelDataReaderLibrary.dll 。 检查此引用后,我想在VBA中创建和使用ExcelDataReader对象,如下所示:

 Sub x() Dim xyz As New ExcelDataReaderLibrary.ExcelDataReader xyz.readSheet "c:\mypath\testfile.xlsx", "Sheet1", "c:\outputPath" End Sub 

该对象已成功创建,但readSheet会出现此错误:

 Automation error The system cannot find the file specified. 

此外, ExcelDataReaderLibrary命名空间还有Intellisense,但ExcelDataReader对象没有Intellisense。 我想我的课程已注册,但不是它的方法 – 我是否必须与Guid做一些不同的事情? 如何从我的VBA代码中调用该方法?

对于问题的第一部分,问题是mscoree.dll需要从调用进程的位置/上下文中找到您的程序集,并且您的对象的程序集通常不在您的进程的文件夹中(技术上,.NET融合)将查看。要告诉.NET在哪里可以找到程序集,您有两种选择:

  • 使用REGASM/codebase参数(如Hans所提到的),它在注册表上留下指向程序集位置的提示; 要么

  • 对程序集进行强签名并将其添加到GAC,其中mscoree.dll将始终查找并找到它。

请注意,程序集的所有依赖项必须同样“可查找”。

我必须警告你,微软非常严厉地阻止人们使用/codebase技术。 他们认为它被设计为开发/调试技术,不应该在生产模式中使用。 我不确定我是否完全理解他们的理由。 您应该查看有关REGASM的文档以及MSDN中的其他参考资料,并对此做出自己的想法。 我个人并不认为将我的对象添加到GAC比使用/codebase更麻烦。

在你的问题的第二部分,关于没有获得IntelliSense:你的问题是,默认情况下,.NET生成的COM类型库暴露纯dispinterface (它们只实现IDispatch ,类型库甚至不会列出dispinterface成员)。 在VB6术语中,您的类作为object公开,并且必须在运行时确定所有方法名称和参数。

COM类的生成由应用于类的属性控制,称为ClassInterfaceAttribute

以下是选项:

  • ClassInterfaceType.AutoDispatch :这是默认值,因此它当前适用于您的类。 您的类作为纯调度接口公开,并且只能是后期绑定。 没有智能感知。 TLB甚至没有列出调度接口成员,因此后期绑定的客户端无法缓存有关您的类暴露的任何详细信息。

这样可以最大限度地灵活地添加,更改和删除成员而不受惩罚,至少在客户端不会造成严重崩溃的情况下。 我称之为“脚本模式”。

  • ClassInterfaceType.None :这是更严格的模式,它要求您更明确地公开您的对象,就像您在IDL / C ++中所做的那样。 您需要使用要公开的方法声明一个或多个接口,并使该类显式inheritance自接口。 如果从多个接口inheritance,第一个接口将被选为[default]接口,但您应该通过ComDefaultInterfaceAttribute明确指定一个ComDefaultInterfaceAttribute 。 如果您不从任何接口inheritance,您的CoClass将直接从IDispatchinheritance。 (作为COM对象公开的所有.NET类都通过IDispatch公开)。

这是我的首选模式,但在COM编程方面我更像是一个传统主义者。 只要您从接口明确声明和inheritance,就可以获得IntelliSense。 显然,您只能调用接口中列出的成员。 我称之为“严格模式”或“IDL模式”。

  • ClassInterfaceType.AutoDual这会自动为您生成一个COM接口,其中包含您要公开的方法的所有详细信息。 这意味着您可以获得IntelliSense,而无需担心创建显式接口。 然而,版本控制是一种巨大的痛苦。 在重新编译和/或重新注册对象之前,您必须非常小心地停止所有客户端,否则如果您的方法签名以任何方式更改,您将处于一个受伤的世界。 我称之为“VB6模式”,因为对我来说它看起来很像VB6为你做的(VB6上的COM版本也是一种皇家的痛苦)。

微软也强烈反对人们不使用AutoDual ,我想这是因为生成的界面的更改可以更容易地发生而不需要你注意。 我实际上还没有机会使用它,但我不确定它是否比None更危险。

总结 :如果您想获得IntelliSense,您需要将[ClassInterface(ClassInterfaceType.None)] (并将您的方法放在显式实现的接口中)或[ClassInterface(ClassInterfaceType.AutoDual)]应用于您的类。 无论哪种方式,在更改assembly之前,您必须非常小心地停止客户(甚至可能“删除”和“重新添加”引用)。