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

<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-компонентав точк! то есть. тот же порядок, те же имена и т. д.

 toxvaerd25 июн. 2009 г., 14:44
Нет, но разработчики Delphi в настоящее время довольно сильно защищены, и они закатывают глаза при мысли о том, чтобы войти в этого старого бегемота; -)
 Scott Langham24 июн. 2009 г., 17:01
Был ли потерян исходный код этой библиотеки?

Ответы на вопрос(7)

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

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

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 неверен.

 toxvaerd26 июн. 2009 г., 11:46
Хорошо - мне удалось создать экземпляр, да, но я не могу привести его к типу интерфейса.
 toxvaerd26 июн. 2009 г., 14:17
На самом деле это не одно и то же ... Я подозревал одно и то же, но восьмая цифра отличается. Парни из Delphi сказали, что именно так Delphi создавал гиды.
 Daniel Earwicker26 июн. 2009 г., 12:37
Right - убедительно указывает на то, что он не поддерживает этот интерфейс, в буквальном смысле. Он может поддерживать только низкоуровневый интерфейс, называемый IDispatch. Пример кода выше работает внутри, используя IDispatch для выполнения вызова. Ты пробовал это
 toxvaerd26 июн. 2009 г., 13:30
Это дает мне «COM цель не реализует IDispatch.». Кроме того, я хотел бы избежать использования отражения.
 Daniel Earwicker26 июн. 2009 г., 14:14
См. Редактирование - это должен быть неправильный IID.
Решение Вопроса

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

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

 toxvaerd25 июн. 2009 г., 14:45
Это выглядит многообещающе ... Я попробую!

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

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

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

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

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

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

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

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

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

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

 Daniel Earwicker26 июн. 2009 г., 11:09
Из-за сообщений об ошибках, которые он получает сейчас, возможно, объект поддерживает ТОЛЬКО IDispatch!
 Rob Kennedy24 июн. 2009 г., 19:20
Это требует, чтобы объект поддерживал позднюю привязку. Не все объекты реализуют IDispatch.

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

Ваш ответ на вопрос