Использование COM DLL из C # без библиотеки типов - PullRequest
11 голосов
/ 24 июня 2009

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

Есть ли способ использовать эту DLL из c # без 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

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

Ответы [ 7 ]

12 голосов
/ 24 июня 2009

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

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

2 голосов
/ 24 июня 2009

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

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

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

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

2 голосов
/ 24 июня 2009

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

1 голос
/ 26 июня 2009

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

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

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

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

(В C # 4.0 это облегчается с помощью ключевого слова dynamic.)

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

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

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

1 голос
/ 24 июня 2009

Да и нет.

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

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

1 голос
/ 24 июня 2009

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

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

0 голосов
/ 24 июня 2009

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

...