使用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
可能会因转换而分配内存(看一看)。 你也不要释放它。