COM-объект, написанный на C# - Получить класс, но не методы

Я написал простой COM-объект в C# только с одним методом, который называется 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

Я могу создать экземпляр объекта (класса) в порядке через следующую строку кода:

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

Если я изменю строку имени, она не будет работать, поэтому я знаю, что эта часть верна.

Однако, когда я пытаюсь вызвать метод следующим образом:

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

... Я получаю исключение во время выполнения (к сожалению, есть проблема с обработкой исключений BCB4 для вызовов OLE, поэтому единственная информация, которую дает мне отладчик, - "Возникло исключение").

Поскольку я могу вызывать другие COM-объекты из того же приложения BCB4 таким же образом, я не думаю, что проблема связана с моим кодом C++. Я думаю, что это проблема либо с C#-созданной COM DLL, либо с ее регистрацией.

Чтобы изучить это, я использовал Microsoft OLE/COM Object Viewer, чтобы просмотреть мою систему на предмет объекта OLE. Я смог найти свой объект как "TRSDotNetCOM.TRSCOM_Class", как и ожидалось.

Я новичок в использовании OLE / COM Object Viewer, поэтому я надеюсь, что я смотрю на правильные вещи ниже:

Когда я расширяю класс, я вижу следующее:Мой класс расширился

Я щелкнул правой кнопкой мыши по _Object и выбрал "View", затем "View Type Info". Затем панель справа показывает:

[   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(); };

Когда я раскрываю дерево слева, вот что я вижу:Расширенный _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-взаимодействия". Я также сгенерировал файл Secure Name с помощью утилиты sn и загрузил файл в разделе настроек Signing.

Так...

1) Я ищу правильное место в OLE / COM Object Viewer для моего метода?

2) Если так, почему мой метод не будет виден или не будет зарегистрирован?

3) Есть идеи, что еще может быть не так?

ОБНОВЛЕНИЕ: Вот обновленный код с предложениями Джо У и Пауло. (Это все еще не работает, однако)

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 ответа

Вам не хватает всего лишь нескольких битов.

Объявите ваши интерфейсы как ComVisible:

[ComVisible(true)]
public interface TRSCOM_Interface

Если ваша сборка уже видна COM по умолчанию (вы можете проверить это в свойствах проекта или, как правило, в AssemblyInfo.cs), вам не нужно этого делать, но это не причинит вреда и сохранит интерфейс доступным для regasm.exe а также tlbexp.exe в случае, если вы вернете эту конфигурацию.

Объявите интерфейс событий как ComImport:

[ComImport]
public interface TRSCOM_Events

Я предполагаю, что этот интерфейс определен вне вашего C# проекта, вероятно, приложением BCB4 или одним из его модулей.

Если мое предположение неверно и ваш C# проект определяет этот интерфейс, то [ComVisible(true)],

Если у этого интерфейса есть методы событий, вы затем реализуете их как события в классе.

Наконец, чтобы избежать экспорта другого интерфейса для вашего класса, вы можете добавить ClassInterface атрибут:

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

Таким образом, вы говорите, что TRSCOM_Interface ваш интерфейс класса по умолчанию, так как он первый, который вы реализуете, и regasm.exe /tlb не будет генерировать интерфейс класса.

В зависимости от порядка реализованных интерфейсов это не обнадеживает, поэтому вы также можете использовать ComDefaultInterface атрибут:

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

Теперь вы можете иметь любой порядок в списке реализованных интерфейсов, не беспокоясь об изменении интерфейса класса по умолчанию.

Это первый раз, когда я видел метод, объявленный ComVisible. Я бы отказался от этого и вместо этого объявил бы интерфейс TRSCOM_Interface ComVisible.

Другие вопросы по тегам