我有一个用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将不会释放它们'但没有细节。
安迪
答案 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可能会分配一个字符数组)
[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
可能会因转换而分配内存(请查看)。你也不要释放它。