3

我有一个用 C# 编写的 dll,并暴露给 COM。我在构建器中使用 dll ...我可以实例化该类,但在编组来自 C# 方法调用的返回值时遇到问题。

C#

public string GetValue([MarshalAs(UnmanagedType.LPWStr)] string key)
{
   return "value";
}

导入构建器时的翻译函数:

virtual HRESULT STDMETHODCALLTYPE GetValue(LPWSTR key/*[in]*/, 
                                           BSTR* pRetVal/*[out,retval]*/) = 0;

我对C++知之甚少。'key' 参数可以很好地传递,因为我可以在参数上使用 'MarshalAs' 属性,但我要么不知道如何为返回值声明它,要么不知道如何调用C++ 端的函数(我已经尝试了几件事,只是猜测)。

更新:好的,我只是以安东的例子并尝试根据汉斯的评论进行修改来解决这个问题。Antons 的回答正如他所展示的那样工作,但由于对内存管理问题的担忧,我最终没有在 C# 中应用 return 属性,并且 C++ 代码调用该函数如下:

BSTR result;
obj->GetValue(key, &result);
SysFreeString(key);
SysFreeString(result);

我希望我能将这两个答案归功于帮助我解决这个问题,它们都是为我提供所需信息所必需的。

4

2 回答 2

7

您可以应用 [return:] 属性,但这是一个非常糟糕的主意。这个函数签名的问题是被调用者必须为字符串分配缓冲区,而调用者必须释放它。这要求两者都使用相同的堆。当您强制它使用 LPWSTR 时,情况并非如此,CLR 使用它自己的堆并且您无法从本机代码中获取它,您无法获得所需的堆句柄。

两段代码必须使用相同的堆。并且有一个专门用于此目的的 COM 堆。BSTR 是使用该堆的字符串类型,CLR 自动使用它,正如您从签名中可以看出的那样。要使用它,只需在调用后访问 pRetVal 指针,它实际上是一个 wchar_t*。然后你必须释放它,调用 SysFreeString()。

于 2012-11-09T00:58:03.580 回答
4

要将属性应用于返回值:

[return: MarshalAs(UnmanagedType.LPWStr)]
public string GetValue([MarshalAs(UnmanagedType.LPWStr)] string key)
{
   return "value";
}

您将需要使用(我认为)手动释放字符串CoTaskMemFree

LPWSTR result ;
if (SUCCEEDED (obj->GetValue (key, &result)))
{
    // use result and free it when no longer needed
    CoTaskMemFree (result) ;
}
于 2012-11-09T00:08:41.913 回答