C# COM 与 C++ COM 返回值
Posted
技术标签:
【中文标题】C# COM 与 C++ COM 返回值【英文标题】:C# COM vs C++ COM return value 【发布时间】:2011-07-25 20:00:57 【问题描述】:我正在构建一个COM组件C#,客户可以设置二进制数据。如果 COM 组件返回异常而不是错误代码会很好,但意识到在(Delphi、C++ 和 JScritp)中处理异常会很困难。我选择它接收十六进制数据(内部转换为二进制)并返回十六进制(内部将二进制转换为十六进制)。 getData 方法可以返回数据和错误代码,问题是:如何在C# 互操作?
在 C++ COM 中存在 HRESULT
HRESULT getData([in] int __position, [out,retval] BSTR* __data); // can Return __data or error -1 data not exists
HRESULT setData([in] BSTR __data, [out,retval] int* __status);
但是在 C# COM 中??
int getData ??? // return __status or __data;
int setData(String __data); // return __status;
先谢谢了,
【问题讨论】:
您的问题在哪里?这里没有问题。 我试图修改措辞。 我想你是想问,你如何控制 COM 接口方法中的 HRESULT 值。请参阅下面的答案。 @Shane Powell,很抱歉造成混乱,不是处理而是扔。我想返回错误代码或数据。异常解决,但在 Delphi、C++ 和 JScript 中很难捕获 C# Interop COM 异常。 Exceptions 是 .net 运行时处理 COM HRESULTS(即错误)的方式。 .net 运行时将 HRESULT 值转换为 from/to 异常。 COM 客户端如何处理 HRESULT 取决于语言运行时。在 C++ 中,您只需获得 HRESULT 并且您必须处理它。在其他运行时会有所不同。 【参考方案1】:C# 将已知的 C# 异常映射到 HRESULT 值。 Here 是微软关于映射列表的文档。您可以通过发送COMException 来获得进一步的控制权。
【讨论】:
嗨@Shane Powell。你是说我可以在 C# Interop 中抛出异常并在(Delphi、C++ 和 JScript)中捕获? 不,我是说你抛出一个异常,.net 运行时将它转换为 HRESULT 返回值。如何在另一侧中断 HRESULT COM 值取决于他们。例如,在 C++ 中,您会获得原始的 HRESULT 值,如果从 C# 调用,它会将 HRESULT 转换回异常。我不知道 Delphi 或 JScript 会发生什么,您必须查看这些语言如何与 COM 互操作。 在delphi中使用safecall,HRESULT会转化为异常。【参考方案2】:您应该从每个 COM 方法返回一个HRESULT
,并在out
参数中返回数据。但是,COM 互操作层通过在 COM HRESULT
错误代码和 C# 异常之间自动转换来为您屏蔽这一点。我认为你的代码应该是这样的:
MIDL
HRESULT getData([out,retval] BSTR* pData);
HRESULT setData([in] BSTR data);
C#
String getData();
void setData(String data);
关键是COM函数的返回值始终是HRESULT
。如果函数成功,则返回 S_OK
— 否则返回描述失败原因的适当错误代码。真正的返回值,在这个例子中,data
,是通过一个 out 参数而不是返回值返回的。但当然,COM 互操作层对您隐藏了实现细节。
您的问题似乎表明您希望在失败的情况下返回值是 HRESULT
,或者在成功的情况下是字符串。这是不可能的,因为函数返回值是静态类型的。
【讨论】:
谢谢,这正是我需要的。因此,该方法可能会返回错误代码或数据。抱歉我的含糊问题。 从技术上讲,您会返回错误代码和数据,但数据仅在错误代码为 S_OK 时才有意义。 S_OK 在 C# 中?我不明白。我在这里尝试过,返回数据和 -1 并且可以工作(C# 到 C++),如果没问题,我将返回 0。int getData(out String __data);
在 COM 中将显示为 HRESULT getData([out] BSTR* __data, [out, retval] int*)
,就像在 msdn.microsoft.com/en-us/library/aa367158%28v=vs.85%29.aspx 中一样。不能将 COM HRESULT 语义与 C# 方法的返回值混用,它们代表不同的东西。
您自己的帖子使用相同的约定,不是吗?关键是 c++ 签名会将 C# 的 int
返回作为输出参数 (int*),而不是作为返回 C/C++ 值。您不能在 C# (S_OK) 中返回一个 int 并期望这是 COM 调用者看到的 HRESULT 值。【参考方案3】:
只需在 C# 中抛出一个异常。 C# 互操作层将知道如何捕获异常,返回 E_FAIL hresult(或其他适当的值,如果 C# 异常已知或明确指定值的 COMException)并在您的 COM 对象上设置 IErrorInfo
。像_com_ptr_t
这样的 C++ 智能 COM 指针知道如何处理这种情况,并将引发适当的 C++ 异常。
见Handling COM Interop Exceptions。
【讨论】:
在 C# 中我做了 public int getData(out String ret) ret = "data";抛出新异常(); return -1; 在 C++ 中尝试 BSTR 测试; int 错误 = objCOM->getData(&test); 捕获(异常 e) 。但不起作用。 objCom 是智能指针 (_com_ptr_t) 还是原始指针?行指针必须走很长的路(查询 IErrorInfo 接口,然后从那里询问错误信息)。使用智能指针,会为您处理好细节。 其实我可能记错了。 _com_ptr_t 不会自动抛出,您仍然需要检查 HRESULT 并明确引发 _com_Error。见codereview.stackexchange.com/questions/259/… 谢谢,我尝试使用 CComPtr 但在 0x7c812afb 出现未处理的异常,这是我的代码:HRESULT errorCheck; ATL::CComPtrcatch(exception e)
将不起作用,因为 CAtlException 不是从 std::exception 派生的。尝试在 C++ 代码中添加 catch(CAtlExceptio& e)
和/或 catch(...)
。【参考方案4】:
这应该可行:
[DllImport(DllName)]
static extern int getData(int __position, StringBuilder __data);
【讨论】:
嗨@SwDevMan81。我没有使用 c++ dll,我只想编写一个可以在 C# COM 中返回(字符串或错误代码)的方法。谢谢。 @Cobaia - 要返回两个值,您可以使用 KeyValuePair(例如:dotnetperls.com/multiple-return-values)。或者您可以使用“out”关键字为参数赋值DLLImport
和 StringBuilder
在这里不合适。这是 COM 互操作而不是 P/invoke。以上是关于C# COM 与 C++ COM 返回值的主要内容,如果未能解决你的问题,请参考以下文章
用C++封装了Halcon的算法,返回值为Hobject类型,在C#中调用dll怎么用Hobject类型的返回值。