Вопрос по interop, c# – Использование COM DLL из C # без библиотеки типов

11

Мне нужно использовать 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-парней:

<code>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.
</code>

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

<code>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);

    }
}
</code>

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

<code>using System.Runtime.InteropServices;

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

ОБНОВИТ

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

Нет, но разработчики Delphi в настоящее время довольно сильно защищены, и они закатывают глаза при мысли о том, чтобы войти в этого старого бегемота; -) toxvaerd
Был ли потерян исходный код этой библиотеки? Scott Langham

Ваш Ответ

7   ответов
1

вы преодолели первое серьезное препятствие!

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

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 является

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

Хорошо - мне удалось создать экземпляр, да, но я не могу привести его к типу интерфейса. toxvaerd
Right - убедительно указывает на то, что он не поддерживает этот интерфейс, в буквальном смысле. Он может поддерживать только низкоуровневый интерфейс, называемый IDispatch. Пример кода выше работает внутри, используя IDispatch для выполнения вызова. Ты пробовал это Daniel Earwicker
Это дает мне «COM цель не реализует IDispatch.». Кроме того, я хотел бы избежать использования отражения. toxvaerd
На самом деле это не одно и то же ... Я подозревал одно и то же, но восьмая цифра отличается. Парни из Delphi сказали, что именно так Delphi создавал гиды. toxvaerd
См. Редактирование - это должен быть неправильный IID. Daniel Earwicker
12

конкретной проблеме в своем блоге:

" Использование неясных интерфейсов Windows COM API в .NET"

Это выглядит многообещающе ... Я попробую! toxvaerd
2

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

Я не знаю VB, и хотел бы не включать больше языков в микс. Должно быть возможно найти решение, используя только C #. Но спасибо за ваш ответ: -) toxvaerd
2

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

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

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

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

1

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

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

1

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

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

Из-за сообщений об ошибках, которые он получает сейчас, возможно, объект поддерживает ТОЛЬКО IDispatch! Daniel Earwicker
Это требует, чтобы объект поддерживал позднюю привязку. Не все объекты реализуют IDispatch. Rob Kennedy
0

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

Похожие вопросы