P/Invoke marshaller sẽ giả định rằng bộ nhớ cho kiểu trả về đã được cấp phát với CoTaskMemAlloc() và sẽ gọi CoTaskMemFree() để giải phóng nó. Nếu điều này đã không được thực hiện, chương trình sẽ thất bại với một ngoại lệ trên Vista và Win7 nhưng âm thầm rò rỉ bộ nhớ trên XP. Sử dụng SysAllocString() có thể được thực hiện để làm việc nhưng bạn phải chú thích kiểu trả về trong thuộc tính [DllImport]. Không làm như vậy sẽ vẫn gây ra rò rỉ, không có chẩn đoán trên Win7. BSTR là không phải là một con trỏ tới khối bộ nhớ được CoTaskMemAlloc phân bổ, có 4 byte ở phía trước địa chỉ được trỏ tới để lưu trữ kích thước chuỗi.
Một trong các kết hợp sau đây sẽ làm việc đúng cách:
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();
Hoặc:
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();
Bạn nên xem xét cho phép các mã khách hàng để vượt qua một bộ đệm vì vậy không có vấn đề quản lý bộ nhớ. Điều đó phải giống như thế này:
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();
Nguồn
2009-12-19 13:47:59
Cảm ơn bạn. Hoạt động tốt. – Alex