Маршаллер P / Invoke предположит, что память для возвращаемого типа была выделена с помощью CoTaskMemAlloc (), и вызовет CoTaskMemFree () для ее освобождения. Если это не было сделано, программа завершится с ошибкой, за исключением Vista и Win7, но тихо утечка памяти в XP. Использование SysAllocString () можно заставить работать, но вы должны аннотировать возвращаемый тип в атрибуте [DllImport]. Несоблюдение этого требования приведет к утечке без диагностики на Win7. BSTR является , а не указателем на блок памяти, выделенный CoTaskMemAlloc, перед указанным адресом имеется 4 байта, в которых хранится размер строки.
Любая из следующих комбинаций будет работать правильно:
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();
Или:
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();
Вы должны разрешить клиентскому коду передавать буфер, чтобы не было проблем с управлением памятью. Это должно выглядеть примерно так:
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();