[英]Pass Interface Array from C# COM Server to C++ Via IDispatch
我正在嘗試開發一個 C# COM 服務器,客戶端只能通過 IDispatch 訪問該服務器。 我的 COM 對象的一些方法返回到其他 COM 對象的接口數組。
當我嘗試使用早期綁定來執行此操作時,例如通過此答案中提供的代碼,它可以正常工作。 但是,一旦我嘗試裝飾我的類型以通過 IDispatch 使用后期綁定,請求就會失敗,並顯示 0x80020005:類型不匹配。 其他非接口類型可以通過 IDispatch 毫無問題地返回,只是 COM 接口無法正確傳輸。
給定以下 C++ 客戶端
CoInitialize(NULL);
IMyRootClassPtr ptr(__uuidof(MyRootClass));
try
{
auto entities = ptr->GetEntities();
}
catch (_com_error &err)
{
wprintf(L"The server throws the error: %s\n", err.ErrorMessage());
wprintf(L"Description: %s\n", (PCWSTR)err.Description());
}
以下代碼適用於早期綁定
C#:
[ComVisible(true)]
public interface IMyRootClass
{
IEmailEntity[] GetEntities();
}
[ComVisible(true)]
public class MyRootClass : IMyRootClass // some class to start with
{
public IEmailEntity[] GetEntities()
{
List<IEmailEntity> list = new List<IEmailEntity>();
for (int i = 0; i < 10; i++)
{
EmailEntity entity = new EmailEntity();
entity.Body = "hello world " + i;
list.Add(entity);
}
return list.ToArray();
}
}
[ComVisible(true)]
public interface IEmailEntity
{
string Body { get; set; }
}
public class EmailEntity : IEmailEntity
{
public string Body { get; set; }
}
通過進行以下更改
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
添加到IMyRootClass
[ClassInterface(ClassInterfaceType.None)]
添加到MyRootClass
我認為這現在應該通過 IDispatch 工作,但是我得到了上面提到的類型不匹配錯誤。 我花了一個下午的時間來調試錯誤的確切來源。 對 .NET 框架的 Dispatch 調用似乎成功,返回 VT_ARRAY 類型的VARIANT
VT_ARRAY | VT_DISPATCH
VT_ARRAY | VT_DISPATCH
(2009),這是預期的,但最終結果的驗證似乎落入了
main
_com_dispatch_method
_com_invoke_helper
VariantChangeType
VariantChangeTypeEx
我已經查看了它在 oleaut32.dll 中掉下來的位置的反匯編,在這個階段我只是得出結論,這一定是 Windows 中的錯誤。 有沒有人可能有其他建議?
請記住, IDispatch
/ 后期綁定是為特殊客戶端創建的(例如:VB/VBA/VBScript/JScript),從純 C/C++ 客戶端使用它總是很痛苦。
使用原始定義,這里是IMyRootClass
的定義方式(您可以使用 RegAsm 生成的 .tlb 文件上的 Windows SDK 中的OleView 工具閱讀):
interface IMyRootClass : IDispatch {
[id(0x60020000)]
HRESULT GetEntities([out, retval] SAFEARRAY(IEmailEntity*)* pRetVal);
};
這將在#import
之后結束,在 C/C++ header 級別:
IMyRootClass : IDispatch
{
//
// Wrapper methods for error-handling
//
SAFEARRAY * GetEntities ( );
//
// Raw methods provided by interface
//
virtual HRESULT __stdcall raw_GetEntities (
/*[out,retval]*/ SAFEARRAY * * pRetVal ) = 0;
};
其中GetEntities
實際上只是圍繞IUnknown
/ early-binding 接口的一個小包裝代碼:
inline SAFEARRAY * IMyRootClass::GetEntities ( ) {
SAFEARRAY * _result = 0;
HRESULT _hr = raw_GetEntities(&_result);
if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
return _result;
}
這就是為什么“雙重”界面很好的原因(不知道為什么你只想要IDispatch
),因為它們允許兩個世界輕松訪問。
現在,如果您更改問題中的定義,則不再定義IMyRootClass
COM 接口。 相反,您只會獲得 IDL 級別的dispinterface
:
dispinterface IMyRootClass {
properties:
methods:
[id(0x60020000)]
SAFEARRAY(IEmailEntity*) GetEntities();
};
這將在#import
之后結束,在 C/C++ header 級別:
IMyRootClass : IDispatch
{
//
// Wrapper methods for error-handling
//
// Methods:
SAFEARRAY * GetEntities ( );
};
其中GetEntities
實際上是一個完全不同的包裝代碼:
inline SAFEARRAY * IMyRootClass::GetEntities ( ) {
SAFEARRAY * _result = 0;
_com_dispatch_method(this, 0x60020000, DISPATCH_METHOD, VT_ARRAY|VT_DISPATCH, (void*)&_result, NULL);
return _result;
}
正如您在此處看到的,由於我們使用的是IDispatch
,因此一切都或多或少接近於 VARIANT 類型(它是同時為這些客戶端發明的),按原樣使用或與包裝器一起使用。
這就是您看到預期返回類型為VT_ARRAY|VT_DISPATCH
的原因。 也可以使用VT_ARRAY|VT_UNKNOWN
或VT_ARRAY|VT_VARIANT
或簡單的VT_VARIANT
(最終包裝器類型),但沒有辦法說VT_ARRAY of IEmailEntity*
。
所以,這個問題至少有兩種解決方案:
1 - 你可以這樣定義你的界面:
[ComVisible(true), InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IMyRootClass
{
object[] GetEntities();
}
[ComVisible(true), ClassInterface(ClassInterfaceType.None)]
public class MyRootClass : IMyRootClass
{
public object[] GetEntities()
{
...
// the Cast forces the creation of an array of object => VT_ARRAY | VT_DISPATCH
return list.Cast<object>().ToArray();
}
}
2 - 或者您可以像這樣“手動”使用IDispatch
接口(不要使用包裝器):
IMyRootClassPtr ptr(__uuidof(MyRootClass));
CComVariant result;
DISPPARAMS p = {};
ptr->Invoke(0x60020000, IID_NULL, 0, DISPATCH_METHOD, &p, &result, nullptr, nullptr);
在這種情況下,結果將是VT_ARRAY | VT_UNKNOWN
VT_ARRAY | VT_UNKNOWN
(與第一種情況一樣),這就是包裝器拋出異常的原因。 comdef.h 的包裝器在其自動化類型支持方面比 VB 等客戶端更受限制。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.