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

时间:2015-03-19 00:04:27

标签: 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调用的异常处理存在问题,所以调试器给我的唯一信息是"异常发生&#34 ;)

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

为了探索这一点,我使用Microsoft OLE / COM对象查看器来浏览我的系统中的OLE对象。我能够按预期找到我的对象" TRSDotNetCOM.TRSCOM_Class&#34 ;.

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

当我扩展课程时,我看到以下内容: My class expanded

我右键单击_Object并选择"查看",然后"查看类型信息"。然后,右侧的窗格显示:

[   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和Paul的更新代码。 (但它仍然不起作用)

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;
        }

    }
}

2 个答案:

答案 0 :(得分:2)

你只缺少几个。

将您的接口声明为ComVisible

[ComVisible(true)]
public interface TRSCOM_Interface

如果您的程序集默认情况下已经可以看到COM(您可以在项目的属性中或通常在AssemblyInfo.cs中查看),那么您不需要这样做,但它没有任何危害,如果您还原此配置,它会保持regasm.exetlbexp.exe的界面可用。

将事件接口声明为ComImport

[ComImport]
public interface TRSCOM_Events

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

如果我猜错了,你的C#项目定义了这个界面,那么[ComVisible(true)]

如果此界面有事件方法,那么您需要implement then as events in the class

最后,为避免为您的班级导出其他界面,您可能需要添加ClassInterface属性:

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

这样,您就知道TRSCOM_Interface是您的类默认界面,因为它是您实现的第一个界面,regasm.exe /tlb无法生成类接口。< / p>

根据实现的接口的顺序不能让人放心,所以你也可以使用ComDefaultInterface属性:

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

现在,您可以在已实现的接口列表中获得任何顺序,而无需担心更改默认类接口。

答案 1 :(得分:0)

这是我第一次看到一个声明为ComVisible的方法。我会放弃它,而是声明TRSCOM_Interface接口ComVisible。