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(); };
Когда я раскрываю дерево слева, вот что я вижу:
Я не вижу своего метода "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.