The P / Invoke router will assume that the memory for the return type has been allocated by CoTaskMemAlloc () and will call CoTaskMemFree () to free it. If this was not done, the program will fail with exception in Vista and Win7, but memory will leak without problems on XP. Using SysAllocString () can be done to work, but you must annotate the return type in the [DllImport] attribute. Not doing this will cause a leak anyway, without diagnostics on Win7. BSTR is not a pointer to the memory block allocated by CoTaskMemAlloc, there are 4 bytes in front of the specified address that store the size of the string.
Any of the following combinations will work correctly:
extern "C" __declspec(dllexport) BSTR __stdcall ReturnsAString() { return SysAllocString(L"Hello world"); } [DllImport(@"c:\projects\cpptemp1\debug\cpptemp1.dll")] [return: MarshalAs(UnmanagedType.BStr)]
Or:
extern "C" __declspec(dllexport) const wchar_t* __stdcall ReturnsAString() { const wchar_t* str = L"Hello world"; wchar_t* retval = (wchar_t*)CoTaskMemAlloc((wcslen(str)+1) * sizeof(wchar_t)); wcscpy(retval, str); return retval; } [DllImport(@"c:\projects\cpptemp1\debug\cpptemp1.dll", CharSet=CharSet.Auto)] private static extern string ReturnsAString();
You should consider allowing the client code to pass a buffer so that there are no problems with memory management. It should look something like this:
extern "C" __declspec(dllexport) void __stdcall ReturnsAString(wchar_t* buffer, size_t buflen) { wcscpy_s(buffer, buflen, L"Hello world"); } [DllImport(@"c:\projects\cpptemp1\debug\cpptemp1.dll", CharSet=CharSet.Auto)] private static extern void ReturnsAString(StringBuilder buffer, int buflen); ... StringBuilder sb = new StringBuilder(256); ReturnsAString(sb, sb.Capacity); string s = sb.ToString();
Hans passant
source share