Использование COM-библиотеки dll из С# без библиотеки типов

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

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

UPDATE: Я попытался реализовать предложение Джеффа, но я получаю эту ошибку:

"Невозможно передать 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
    {
    }
}

UPDATE

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

Ответ 2

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

Ответ 3

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

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

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

Вот ссылка на учебное пособие по потреблению интерфейсов, которые не основаны на TLB. Учебник

Ответ 4

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

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

Ответ 5

Да и нет.

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

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

Ответ 6

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

Ответ 7

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

Теперь попробуйте следующее:

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

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

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

EDIT: Просто заметил что-то очень подозрительное. IID для интерфейса и CLSID для самого объекта кажутся одинаковыми. Это не так.

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

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