已成功注册COM DLL,但不能使用类方法 [英] Successfully registered COM DLL, but can't use class methods

查看:121
本文介绍了已成功注册COM DLL,但不能使用类方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I registered a COM DLL using regasm.exe, and now I'm trying to write a VBA script that uses a class from the DLL. The DLL is ExcelDataReaderLibrary.dll. In the C# source, the class is described as follows (includes code from this library):

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 文件包括以下内容:

My assembly.cs file includes the following:

[assembly: ComVisible(true)]

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

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

When I open the VBA Editor and go to References, I see a reference for ExcelDataReaderLibrary. Note that the file associated with this reference is ExcelDataReaderLibrary.tlb, not ExcelDataReaderLibrary.dll. After I check this reference, I want to create and use an ExcelDataReader object in VBA as follows:

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

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

The object is successfully created, but readSheet gives this error:

Automation error
The system cannot find the file specified.

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

Also, there is Intellisense for the ExcelDataReaderLibrary namespace, but there is no Intellisense for the ExcelDataReader object. I guess my class is registered but not its method--do I have to do something different with the Guid? How can I call the method from my VBA code?

推荐答案

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

For the first part of your question, the problem is that mscoree.dll needs to find your assembly from the location/context of the calling process, and your object's assembly is not usually in a folder that your process (technically, .NET fusion) will look in. To tell .NET where to find the assembly, you have two options:


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

  • Use the /codebase parameter of REGASM (as Hans mentioned), which leaves a hint on the registry that points to the assembly's location; or

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

Strong-sign your assembly and add it to the GAC, where mscoree.dll will always look and find it.

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

Notice that all the dependencies of your assembly must be equally "findable".

我必须警告您,微软非常严厉地劝阻人们不要使用 / codebase 技术。他们认为它被设计为开发/调试技术,因此不应在生产模式下使用。我不确定我是否完全理解他们的理由。您可能应该研究 REGASM 的文档以及MSDN中的其他参考,并对此下定决心。与使用 / codebase 相比,我个人发现将对象添加到GAC并不麻烦。

I must warn you that Microsoft very sternly discourages people from using the /codebase technique. They argue that it is designed as a development/debugging technique and that it should not be used in production mode. I'm not sure I fully understand their rationale. You should probably look into the documentation for REGASM and other references in MSDN and make up your own mind about this. I don't personally find adding my objects to the GAC any more onerous than using /codebase.

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

On the second part of your question, about not getting IntelliSense: your problem is that, by default, COM type libraries generated by .NET expose pure dispinterfaces (they implement only IDispatch, and the type library won't even list the dispinterface members). In VB6 terminology, your classes are exposed as objects, and all method names and parameters must be determined at run-time.

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

The generation of the COM class is controlled by an attribute applied to your class, called ClassInterfaceAttribute.

以下是这些选项:


  • ClassInterfaceType.AutoDispatch :这是默认设置,因此当前适用于您的班级。您的课程作为纯Dispinterface公开,并且只能是后期绑定。没有IntelliSense。 TLB甚至没有列出Dispinterface成员,因此后期绑定客户端无法缓存有关您的类公开内容的任何详细信息。

  • ClassInterfaceType.AutoDispatch: This is the default and so it applies currently to your class. Your class is exposed as a pure dispinterface, and can only be late-bound. No IntelliSense. The TLB doesn't even list the dispinterface members, so a late-bound client cannot ever cache any details about what your class exposes.

这可以最大程度地灵活地添加,更改和删除成员而不受惩罚,至少在不影响客户的情况下如此。我称之为脚本模式。

This allows maximum flexibility to add, change and remove members with impunity, at least insofar as not hard-crashing the client is concerned. I call this "scripting mode".


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

  • ClassInterfaceType.None: This is the stricter mode, that asks you to expose your object more explicitly, kind of like you would have done in IDL/C++. You are expected to declare one of more interfaces with the methods that you want to expose, and make the class explicitly inherit from the interface(s). If you inherit from more than one interface, the first one will be picked as the [default] interface, but you should probably designate one explicitly via the ComDefaultInterfaceAttribute. If you don't inherit from any interface, your CoClass will inherit straight from IDispatch. (All .NET classes exposed as COM objects are exposed via IDispatch).

这是我的首选模式,但是我更喜欢COM编程方面的传统主义者。只要您确实声明并从接口继承,您就可以获得IntelliSense。显然,您只能调用接口中列出的成员。我称之为严格模式或 IDL模式。

This is my preferred mode, but I'm more of a traditionalist when it comes to COM programming. You do get IntelliSense, as long as you do declare and inherit explicitly from an Interface. Obviously, you only get to call the members listed in the Interface. I call this "Strict mode" or "IDL mode".


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

  • ClassInterfaceType.AutoDual This produces a COM interface automatically for you, one that includes all the details of the methods you are exposing. That means you get IntelliSense and you don't have to worry about creating an explicit interface. However, versioning is a royal pain. You have to be very careful to stop all clients before recompiling and/or re-registering your object, or you will be in a world of hurt if your method signatures changed in any way. I call this "VB6 mode", because to me it looks a lot like what VB6 did for you (COM versioning on VB6 is also a royal pain).

Microsoft还强烈劝阻人们不要使用 AutoDual ,因为对生成的界面的更改可以更容易地发生,而无需您注意。我实际上还没有机会使用它,但是我不确定它是否比 None 危险得多。

Microsoft also strongly discourages people from using AutoDual, I suppose because changes to the generated interface can happen more easily without you noticing. I haven't actually had a chance to use it yet, but I'm not sure that it is that much more dangerous than None.

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

In summary: If you want to get IntelliSense, you need to apply to your class either [ClassInterface(ClassInterfaceType.None)] (and put your methods in an interface that you explicitly implement), or [ClassInterface(ClassInterfaceType.AutoDual)]. Either way, you have to be very careful to stop your clients (and maybe even "removing" and "re-adding" the references) before making changes to your assembly.

这篇关于已成功注册COM DLL,但不能使用类方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆