The P/Invoke marshaller will assume that memory for the return type was allocated with CoTaskMemAlloc() and will call CoTaskMemFree() to release it. If this was not done, the program will fail with an exception on Vista and Win7 but silently leak memory on XP. Using SysAllocString() can be made to work but you have to annotate the return type in the [DllImport] attribute. Not doing so will still cause a leak, without a diagnostic on Win7. A BSTR is not a pointer to a memory block allocated by CoTaskMemAlloc, there are 4 bytes in front of the pointed-to address that store the string size.
Either of the following combinations will work properly:
extern "C" __declspec(dllexport)
BSTR __stdcall ReturnsAString() {
return SysAllocString(L"Hello world");
}
[DllImport(@"c:\projects\cpptemp1\debug\cpptemp1.dll")]
[return: MarshalAs(UnmanagedType.BStr)] // NOTE: required!
private static extern string ReturnsAString();
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 there are no memory management issues. That ought to look similar to 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();