簡體   English   中英

在沒有類型庫的情況下使用C#中的COM dll

[英]Using a COM dll from C# without a type library

我需要使用很久以前在Delphi中開發的COM組件(dll)。 問題是:dll不包含類型庫...並且.NET中的每個互操作功能(例如,TlbImp)似乎都依賴於TLB。 這個組件已經在Delphi程序中使用了很多年沒有問題,因為“使用Delphi的COM對象並不是什么問題,因為我們知道接口”(引用Delphi開發人員)。

有沒有辦法在沒有TLB的情況下從c#中使用這個DLL? 我嘗試使用DLL作為非托管,但它導出的唯一方法是DllUnregisterServerDllRegisterServerDllCanUnloadNowDllGetClassObject 我知道我將要使用的類和函數的名稱,如果這可以有任何幫助。

更新:我已經嘗試過實施Jeff的建議,但是我收到了這個錯誤:

“無法將'ComTest.ResSrvDll'類型的COM對象強制轉換為接口類型'ComTest.IResSrvDll'。此操作失敗,因為對於具有IID'{75400500-939F-11D4-9E44-0050040CE72C}的接口的COM組件上的QueryInterface調用'由於以下錯誤而失敗:不支持此類接口(來自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);

    }
}

而這個coclass(得到了德爾福人的指導)

using System.Runtime.InteropServices;

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

UPDATE

Jeff的解決方案是實現這一目標的方法。 但值得注意的是,接口定義必須與COM組件完全匹配! 即。 相同的順序,相同的名稱等

您只需要CLS_ID和接口ID。 我在博客上寫過這個具體問題:

在.NET中使用模糊的Windows COM API

在VB.Net中寫一個包裝器。 VB.Net支持真正的后期綁定(沒有凌亂的反射)。 你需要的只是progId。 您還應該實現IDisposable以明確管理組件生命周期。

您經常遇到一個沒有類型庫(Delphi或其他)支持的接口實現。 Shell擴展就是一個例子。

您基本上需要通過正確的COM函數調用進行Windows API調用來創建實例。 API將通過您之前提到的導出函數來管理DLL。

您需要在C#代碼中重新創建接口定義,但之后您只需創建對象,將其強制轉換為接口,它與其他任何東西都沒有區別。 這里唯一真正的警告是,根據您的使用情況,您可能會遇到一些線程問題需要處理,因此請檢查用於DLL的“線程模型”並考慮您的使用情況。

這是一個關於使用非基於TLB的接口的教程的鏈接。 教程

您也可以進行后期綁定 ,然后通過反射調用方法( myObject.InvokeMember("NameOfTheMethod", options, params, etc.) )。

但是,包裝器應該提供更好的性能和更快的編組。

是的,不是。

所有C#(和任何CLR語言)需要與COM對象進行通信是兼容的接口簽名。 通常指定接口的方法,GUID和公寓樣式。 如果您可以將此定義添加到代碼庫中,則不需要TLB。

該陳述附帶一個小警告。 我相信如果您嘗試在公寓邊界使用COM對象並且沒有注冊合適的TLB,您將遇到麻煩。 我不能百分之百地記住這一點。

如果你已經設法創建了一個對象的實例,那么你已經超越了第一個主要障礙!

現在試試這個:

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人員並要求他們告訴你IResSrvDll接口的IID是什么。

再次編輯:您可以嘗試從ComInterfaceType更改指定的枚舉成員。 應該有IDispatch和“dual” - 雖然你的對象不支持IDispatch ,但這些都不是正確的選擇。 IUnknown設置(顯示在示例代碼中)應該有效 - 表明IID是錯誤的。

我懷疑dynamic關鍵字(C#4.0)會實現這一點。 如果是這樣,它將給出與調用方法大致相當的結果,即Groo如何建議。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM