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

我有一个用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#客户端。 但是每次往返服务器都会泄漏服务器进程中的内存。 crt泄漏检测支持显示crt堆上没有重大泄漏,所以我怀疑泄漏是用IMalloc分配的。

我在这做错了吗? 我发现含糊的评论说“所有参数必须用CoTaskMemAlloc分配,否则互操作编组将不会释放它们”,但没有细节。

安迪

阿内尔森已经很好地介绍了这一点,但我想补充几点;

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

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

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

  • 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; } 

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

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

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

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

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

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

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