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();