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::CComPtr objCOM; CoInitialize(NULL); errorCheck = objCOM.CoCreateInstance(CLSID_MyClassCOM, 0, CLSCTX_INPROC_SERVER); if (SUCCEEDED(errorCheck)) try BSTR test; int 错误 = objCOM->getData(&test); catch(exception e) ... C# 抛出新异常,C++ 无法处理。 C# 代码: public int getData(out String ret) ret = "data";抛出新异常();返回-1; CComPtr 抛出 CAtlException 我认为,所以 catch(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”关键字为参数赋值 DLLImportStringBuilder 在这里不合适。这是 COM 互操作而不是 P/invoke。

以上是关于C# COM 与 C++ COM 返回值的主要内容,如果未能解决你的问题,请参考以下文章

在 C# 中添加 COM 引用生成的代理存根时方法的返回值

用C++封装了Halcon的算法,返回值为Hobject类型,在C#中调用dll怎么用Hobject类型的返回值。

将字符串值从 C# 返回到 c++

使用 COM 接口 C# 从 c++ 访问会产生错误

为啥 C++ 从 C# 中为返回自定义结构的函数获取错误的参数值

c++ OpenProcess() 函数返回值为啥返回NULL 急需解决!!! 邮箱372199852@qq.com