c# - 用 C# 编写的 COM 对象 - 获取类,但不获取方法

标签 c# c++ com ole regasm

我用 C# 编写了一个简单的 COM 对象,只有一个方法,称为 GetMac。我无法让它工作。我正在尝试从旧版 Borland C++ Builder 4 (BCB4) 应用程序访问它,我知道该应用程序很旧,并且不再使用太多,但我可以从中访问其他 COM 对象。

Borland 开发机器运行的是 Windows XP,因此我使 C# COM 对象面向 .NET 4.0 框架。我将 DLL 和 PDB 文件从 C# Visual Studio 计算机复制到 XP 计算机。我通过以下命令注册了它:

"%WINDIR%\Microsoft.NET\Framework\v4.0.30319\regasm.exe" TRSDotNetCOM.dll /tlb /nologo /codebase

我可以通过以下代码行很好地实例化 COM 对象(类):

Variant TDN = CreateOleObject("TRSDotNetCOM.TRSCOM_Class");

如果我更改名称字符串,它就不起作用,所以我知道我的这部分是正确的。

但是,当我尝试按如下方式调用该方法时:

MacV = TDN.OleFunction(funcNameV,counterV,macKeyV);

...我收到运行时异常(不幸的是,BCB4 对 OLE 调用的异常处理存在问题,因此调试器给我的唯一信息是“发生异常”)。

由于我能够以相同的方式从同一 BCB4 应用程序调用其他 COM 对象,因此我认为问题不在于我的 C++ 代码。我认为这是 C# 创建的 COM DLL 或其注册的问题。

为了探索这一点,我使用 Microsoft OLE/COM 对象查看器来浏览我的系统以查找 OLE 对象。正如预期的那样,我能够找到“TRSDotNetCOM.TRSCOM_Class”对象。

我在使用 OLE/COM 对象查看器方面是全新的,所以我希望我正在查看以下正确的内容:

当我扩展类(class)时,我看到以下内容: My class expanded

我右键单击 _Object 并选择“查看”,然后选择“查看类型信息”。然后,右侧 Pane 显示:

[   uuid(65074F7F-63C0-304E-AF0A-D51741CB4A8D),   hidden,   dual,   nonextensible,
    custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "System.Object")

] dispinterface _Object {
    properties:
    methods:
        [id(00000000), propget,
            custom({54FC8F55-38DE-4703-9C4E-250351302B1C}, "1")]
        BSTR ToString();
        [id(0x60020001)]
        VARIANT_BOOL Equals([in] VARIANT obj);
        [id(0x60020002)]
        long GetHashCode();
        [id(0x60020003)]
        _Type* GetType(); };

当我展开左侧的树时,我看到的是: Expanded _Object

我没有看到我的方法“GetMac”在其中列出。因此,我认为该方法对 COM 不可见,或者它没有通过 regasm 注册。

这是 COM 对象的源代码:

using System;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;

namespace TRSDotNetCOM
{
    [Guid("80ef9acd-3a75-4fcd-b841-11199d827e8f")]
    public interface TRSCOM_Interface
    {
        [DispId(1)]
        string GetMac(string counter, string macKey);
    }

    // Events interface Database_COMObjectEvents 
    [Guid("67bd8422-9641-4675-acda-3dfc3c911a07"),
    InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface TRSCOM_Events
    {
    }


    [Guid("854dee72-83a7-4902-ab50-5c7a73a7e17d"),
    ClassInterface(ClassInterfaceType.None),
    ComVisible(true),
    ComSourceInterfaces(typeof(TRSCOM_Events))]
    public class TRSCOM_Class : TRSCOM_Interface
    {
        public TRSCOM_Class()
        {
        }

        [ComVisible(true)]
        public string GetMac(string counter, string macKey)
        {
            // convert counter to bytes
            var counterBytes = Encoding.UTF8.GetBytes(counter);
            // import AES 128 MAC_KEY
            byte[] macKeyBytes = Convert.FromBase64String(macKey);
            var hmac = new HMACSHA256(macKeyBytes);
            var macBytes = hmac.ComputeHash(counterBytes);
            var retval = Convert.ToBase64String(macBytes);
            return retval;
        }

    }
}

我确实确认并进入项目属性并选中“注册 COM 互操作”复选框。我还使用“sn”实用程序生成了一个安全名称文件,并将该文件加载到设置的“签名”部分中。

所以...

1) 我是否在 OLE/COM 对象查看器中的正确位置查找我的方法?

2)如果是这样,为什么我的方法不可见或未注册?

3)还有什么想法可能是错误的吗?

更新:这是根据 Joe W 和 Paulo 的建议更新的代码。 (但是仍然不起作用)

using System;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;

namespace TRSDotNetCOM
{
    [Guid("80ef9acd-3a75-4fcd-b841-11199d827e8f"),
    ComVisible(true)]
    public interface TRSCOM_Interface
    {
        [DispId(1)]
        string GetMac(string counter, string macKey);
    }

    // Events interface Database_COMObjectEvents 
    [Guid("67bd8422-9641-4675-acda-3dfc3c911a07"),
    ComImport,
    ComVisible(true),
    InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface TRSCOM_Events
    {
    }


    [Guid("854dee72-83a7-4902-ab50-5c7a73a7e17d"),
    ClassInterface(ClassInterfaceType.None),
    ComDefaultInterface(typeof(TRSCOM_Interface)),
    ComVisible(true),
    ComSourceInterfaces(typeof(TRSCOM_Events))]
    public class TRSCOM_Class : TRSCOM_Interface
    {
        public TRSCOM_Class()
        {
        }


        public string GetMac(string counter, string macKey)
        {
            // convert counter to bytes
            var counterBytes = Encoding.UTF8.GetBytes(counter);
            // import AES 128 MAC_KEY
            byte[] macKeyBytes = Convert.FromBase64String(macKey);
            var hmac = new HMACSHA256(macKeyBytes);
            var macBytes = hmac.ComputeHash(counterBytes);
            var retval = Convert.ToBase64String(macBytes);
            return retval;
        }

    }
}

最佳答案

你只遗漏了一些内容。

将您的接口(interface)声明为 ComVisible:

[ComVisible(true)]
public interface TRSCOM_Interface

如果您的程序集默认情况下已经是 COM 可见的(您可以在项目的属性中或通常在 AssemblyInfo.cs 中检查这一点),则不需要执行此操作,但这不会造成任何损害,并且会保留接口(interface)可用于 regasm.exetlbexp.exe,以防您恢复此配置。

将事件接口(interface)声明为 ComImport:

[ComImport]
public interface TRSCOM_Events

我的猜测是,该接口(interface)是在 C# 项目之外定义的,可能是由 BCB4 应用程序或其模块之一定义的。

如果我的猜测是错误的,并且您的 C# 项目是定义此接口(interface)的项目,则使用 [ComVisible(true)]

如果这个接口(interface)有事件方法,那么 implement then as events in the class .

最后,为了避免为您的类导出另一个接口(interface),您可能需要添加 ClassInterface属性:

[ClassInterface(ClassInterfaceType.None)]
public class TRSCOM_Class : TRSCOM_Interface

这样,您就可以告诉 TRSCOM_Interface 是您的类默认接口(interface),因为它是您实现的第一个接口(interface),而 regasm.exe/tlb 不会生成一个类接口(interface)。

依赖于实现接口(interface)的顺序并不令人放心,因此您还可以使用 ComDefaultInterface 属性:

[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(TRSCOM_Interface))]
public class TRSCOM_Class : TRSCOM_Interface

现在,您可以在已实现的接口(interface)列表中按任意顺序排列,而不必担心更改默认类接口(interface)。

关于c# - 用 C# 编写的 COM 对象 - 获取类,但不获取方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29134942/

相关文章:

c# - 为什么它不遵循格式化程序的模板

c++ - 在 C++ 或 Delphi 中播放 .mod 音频文件的库

.net - 使用OCX无需注册

android - 使用 ADB shell 命令从 Android 设备读取短信

c++ - DirectShow 源过滤器

c# - 从 app.config 文件的 web.config 部分获取值?

c# - Windows Phone 7 芒果使用音频流

c# - 在 IList 上使用 Linq 方法 .Any(<predicate>) 和 .All(<predicate>) 时,谓词是否按严格顺序应用于列表?

c++ - 这个 C++ 隐式转换是如何发生的?

C++模板成员函数 "identifier not found"