使用COM互操作编组从C ++到C#的BSTR

时间:2009-08-19 13:57:00

标签: c# c++ com interop atl

我有一个用C ++编写的进程外COM服务器,它由一些C#客户端代码调用。其中一个服务器接口上的方法将大型BSTR返回给客户端,我怀疑这会导致内存泄漏。该代码有效,但我正在寻找有关编组BSTR的帮助。

简化一下,服务器方法的IDL是

HRESULT ProcessRequest([in] BSTR request, [out] BSTR* pResponse);

,实现如下:

HRESULT MyClass::ProcessRequest(BSTR request, BSTR* pResponse)
{
    USES_CONVERSION;
    char* pszRequest = OLE2A(request);
    char* pszResponse = BuildResponse(pszRequest);
    delete pszRequest;
    *pResponse = A2BSTR(pszResponse);
    delete pszResponse;
    return S_OK;
}

A2BSTR在内部使用SysAllocStringLen()分配BSTR。

在C#客户端中,我只需执行以下操作:

string request = "something";
string response = "";
myserver.ProcessRequest(request, out response);
DoSomething(response);

这样做,因为请求字符串被发送到COM服务器并且正确的响应字符串被返回给C#客户端。但是每次往返服务器都会泄漏 server 进程中的内存。 crt泄漏检测支持显示crt堆上没有明显的泄漏,所以我怀疑泄漏是用IMalloc分配的。

我在这里做错了吗?我发现含糊不清的评论说'所有输出的参数必须用CoTaskMemAlloc分配,否则interop marshaller将不会释放它们'但没有细节。

安迪

3 个答案:

答案 0 :(得分:3)

我没有看到您的代码存在明显问题。建议您修改ProcessRequest方法以排除COM interop作为泄漏源:

HRESULT MyClass::ProcessRequest(BSTR request, BSTR* pResponse)
{
    *psResponse = ::SysAllocStringLen(L"[suitably long string here]");
    return S_OK;
}

我怀疑不会泄漏,在这种情况下,你已经缩小了代码的漏洞。

我还注意到OLE2A在堆栈上分配内存,因此不仅不应该删除pszRequest,而且由于堆栈溢出的可能性,你根本不应该使用OLE2A。有关更安全的替代方案,请参阅this article

我还建议您用:: SysAllocString(CA2W(pszResponse))替换A2BSTR

答案 1 :(得分:2)

anelson已经很好地介绍了这一点,但我想补充几点;

  • CoTaskMemAlloc不是唯一的COM友好分配器 - BSTR由默认的编组器识别,并将使用SysAllocString&amp ;;释放/重新分配。朋友。

  • 避免使用USES_CONVERSION(由于堆栈溢出风险 - 请参阅anelson的回答),您的完整代码应该是这样的[1]

(注意A2BSTR可以安全使用,因为它在转换后调用SysAllocString,并且不使用动态堆栈分配。另外,使用array-delete(delete []),因为BuildResponse可能会分配一个字符数组)

  • BSTR分配器有一个缓存,可以使其看起来好像存在内存泄漏。有关详细信息,请参阅http://support.microsoft.com/kb/139071;有关OANOCACHE,请参阅Google。您可以尝试禁用缓存,看看“泄漏”是否消失。

[1]

HRESULT MyClass::ProcessRequest(BSTR request, BSTR* pResponse)
{
    char* pszResponse = BuildResponse(CW2A(request));
    *pResponse = A2BSTR(pszResponse);
    delete[] pszResponse;
    return S_OK;
}

答案 2 :(得分:-4)

我猜你需要用request销毁::SysFreeString()。该内存在服务器端分配。

此外,OLE2A可能会因转换而分配内存(请查看)。你也不要释放它。