[英]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作為非托管,但它導出的唯一方法是DllUnregisterServer
, DllRegisterServer
, DllCanUnloadNow
和DllGetClassObject
。 我知道我將要使用的類和函數的名稱,如果這可以有任何幫助。
更新:我已經嘗試過實施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。 我在博客上寫過這個具體問題:
在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.