Использование COM DLL из C# без библиотеки типов

Мне нужно использовать COM-компонент (DLL), разработанный в Delphi давным-давно. Проблема в том, что dll не содержит библиотеки типов... и каждая функция взаимодействия (например, TlbImp) в.NET, похоже, полагается на TLB. Компонент использовался здесь в программах Delphi в течение многих лет без проблем, потому что "Использование объектов COM из Delphi не представляет большой проблемы, потому что мы знаем интерфейсы" (цитата из Delphi Developer).

Есть ли способ, которым я могу использовать эту DLL из C# без TLB? Я пытался использовать DLL как неуправляемую, но единственный метод, который она экспортирует, это DllUnregisterServer, DllRegisterServer, DllCanUnloadNow а также DllGetClassObject, Я знаю названия классов и функций, которые я собираюсь использовать, если это может помочь.

ОБНОВЛЕНИЕ: я попытался реализовать предложение Джеффа, но я получаю эту ошибку:

"Невозможно привести объект COM типа" ComTest.ResSrvDll "к типу интерфейса" ComTest.IResSrvDll ". Эта операция завершилась неудачно, поскольку вызов QueryInterface для компонента COM для интерфейса с IID" {75400500-939F-11D4-9E44-0050040CE72C}'не удалось из-за следующей ошибки: такой интерфейс не поддерживается (Исключение из HRESULT: 0x80004002 (E_NOINTERFACE))."

Вот что я сделал:

Я получил это определение интерфейса от одного из Delphi-парней:

unit ResSrvDllIf;

interface

type
   IResSrvDll = interface
   ['{75400500-939F-11D4-9E44-0050040CE72C}']
    procedure clearAll;

    function  ResObjOpen(const aClientID: WideString; const aClientSubID: WideString;
                         const aResFileName: WideString; aResShared: Integer): Integer; {safecall;}
    ...
   end;
implementation
end.

Из этого я сделал этот интерфейс

using System.Runtime.InteropServices;
namespace ComTest
{
    [ComImport]
    [Guid("75400500-939F-11D4-9E44-0050040CE72C")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IResSrvDll
    {
        int ResObjOpen(string aClientID, string aClientSubID, string aResFileName, int aResShared);

    }
}

И этот класс (получил гид от дельфи-парней)

using System.Runtime.InteropServices;

namespace ComTest
{
    [ComImport]
    [Guid("75400503-939F-11D4-9E44-0050040CE72C")]
    public class ResSrvDll
    {
    }
}

ОБНОВИТЬ

Решение от Джеффа - способ сделать это. Однако стоит отметить, что определение интерфейса должно точно соответствовать COM-компонентам! то есть. тот же порядок, те же имена и т. д.

7 ответов

Решение

Вам просто нужно CLS_ID и идентификатор интерфейса. Я написал об этой конкретной проблеме в своем блоге:

" Использование Obscure Windows COM API в.NET"

Если вам удалось создать экземпляр объекта, вы преодолели первое серьезное препятствие!

Теперь попробуйте это:

myObject.GetType().InvokeMember(
                      "ResObjOpen",  // method name goes here
                      BindingFlags.InvokeMethod,
                      null,
                      myObject,
                      new object[] { 
                         someClientID,   // arguments go here
                         someSubId, 
                         somFileName, 
                         someInt} );

Я думаю, что вам может потребоваться сделать это потому, что объект Delphi COM не является "двойным" объектом. Он может поддерживать только позднюю привязку, то есть вид вызова, который вы видите выше.

(В C# 4.0 они делают это проще с dynamic ключевое слово.)

РЕДАКТИРОВАТЬ: Просто заметил что-то очень подозрительное. IID для интерфейса и CLSID для самого объекта выглядят одинаково. Это не правильно.

Учитывая, что вам удалось создать объект, он будет выглядеть как CLSID объекта. Так что это не правильный IID. Вам нужно вернуться к своим людям в Delphi и попросить их рассказать, что такое IID интерфейса. IResSrvDll является.

Изменить снова: вы можете попробовать изменить член enum, который вы указали ComInterfaceType, Там должны быть для IDispatch и "двойной" - хотя ваш объект не поддерживает IDispatchни один из них не должен быть правильным выбором. IUnknown настройка (которая появляется в вашем примере кода) должна работать - предполагая, что IID неверен.

Написать обертку в VB.Net. VB.Net поддерживает истинную позднюю привязку (без грязного отражения). Все, что вам нужно, это progId. Вы также должны реализовать IDisposable для явного управления жизненным циклом компонента.

Очень часто вы сталкиваетесь с реализацией интерфейса, которая не поддерживается библиотекой типов (Delphi или иным образом). Расширения оболочки являются одним из примеров.

В основном вам нужно сделать вызов API Windows, чтобы создать экземпляр с помощью соответствующих вызовов функций COM. API позаботится об управлении DLL через экспортированные функции, которые вы упомянули ранее.

Вам нужно будет воссоздать определение интерфейса в коде C#, но после этого вы просто создаете объект, приводите его к интерфейсу, и он ничем не отличается от всего остального. Единственное реальное предостережение: в зависимости от вашего использования у вас могут возникнуть проблемы с многопоточностью, поэтому проверьте "модель многопоточности", которая использовалась для DLL, и рассмотрите возможность ее использования.

Вот ссылка на руководство по использованию интерфейсов, которые не основаны на TLB. Руководство

И да и нет.

Все, что нужно C# (и любому языку CLR) для связи с COM-объектом, - это подпись совместимого интерфейса. Типично указание методов, GUID и стиля квартиры интерфейса. Если вы можете добавить это определение в свою кодовую базу, тогда TLB не нужен.

С этим утверждением связана небольшая оговорка. Я полагаю, что у вас возникнут проблемы, если вы попытаетесь использовать COM-объект за пределами квартир и не зарегистрируете подходящий TLB. Я не могу вспомнить 100% по этому вопросу.

Вы также можете выполнить позднее связывание, а затем вызывать методы через отражение (myObject.InvokeMember("NameOfTheMethod", options, params, etc.)).

Обертка должна, однако, предлагать лучшую производительность и более быстрое сортирование.

Я подозреваю, что dynamic ключевое слово (C# 4.0) выполнит это. Если это произойдет, это даст результаты, которые в значительной степени эквивалентны вызову методов, то есть, как предлагает Груо.

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