[英]Convention for passing BSTRs into COM functions from C# (COM interop)
[英]Marshalling BSTRs from C++ to C# with COM interop
我有一個用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分配,否則互操作編組將不會釋放它'但沒有細節。
安迪
我沒有看到您的代碼存在明顯問題。 建議您修改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
阿內爾森已經很好地介紹了這一點,但我想補充幾點;
CoTaskMemAlloc不是唯一的COM友好分配器 - BSTR由默認的編組器識別,並將使用SysAllocString和朋友釋放/重新分配。
避免使用USES_CONVERSION(由於堆棧溢出風險 - 請參閱anelson的回答),您的完整代碼應該是這樣的[1]
(請注意,A2BSTR可以安全使用,因為它在轉換后調用SysAllocString,並且不使用動態堆棧分配。另外,使用array-delete(delete []),因為BuildResponse可能會分配一個chars數組)
[1]
HRESULT MyClass::ProcessRequest(BSTR request, BSTR* pResponse)
{
char* pszResponse = BuildResponse(CW2A(request));
*pResponse = A2BSTR(pszResponse);
delete[] pszResponse;
return S_OK;
}
我猜你需要用::SysFreeString()
來銷毀request
。 該內存在服務器端分配。
此外, OLE2A
可能會因轉換而分配內存(看一看)。 你也不要釋放它。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.