使用平台Invoke(C#)在非托管代码中分配和释放内存

我想在非托管代码(C ++)中分配和释放内存,我们从托管代码(C#)中将它们称为函数。 我不确定以下代码是否没有内存泄漏?

C#代码:

[DllImport("SampleDLL.dll")] public extern void getString([MarshalAs(UnmanagedType.LPStr)] out String strbuilder); [DllImport("SampleDLL.dll")] public extern void freeMemory([MarshalAs(UnmanagedType.LPStr)] out String strBuilder); .... //call to unmanaged code getString(out str); Console.WriteLine(str); freeMemory(out str); 

C ++代码:

 extern void _cdecl getString(char **str) { *str = new char[20]; std::string temp = "Hello world"; strncpy(*str,temp.c_str(),temp.length()+1); } extern void _cdecl freeMemory(char **str) { if(*str) delete []*str; *str=NULL; } 

不,这不行。 pinvoke marshaller将尝试使用CoTaskMemFree()释放字符串的内存。 它不知道你有一个释放function。 这不会很好,你没有用CoTaskMemAlloc分配字符串。 这将是XP中的无声内存泄漏,Vista中的崩溃和上升。

你必须阻止编组人员做正确的工作:

 [DllImport("SampleDLL.dll")] public extern void getString(out IntPtr strptr); [DllImport("SampleDLL.dll")] public extern void freeMemory(IntPtr strptr); 

然后,需要在C#代码中使用Marshal.PtrToStringAnsi()从返回的指针自己封送字符串。

我个人认为使用BSTR最容易做到这一点,从而避免了导出解除分配器的需要。

C ++

 BSTR ANSItoBSTR(const char* input) { BSTR result = NULL; int lenA = lstrlenA(input); int lenW = ::MultiByteToWideChar(CP_ACP, 0, input, lenA, NULL, 0); if (lenW > 0) { result = ::SysAllocStringLen(0, lenW); ::MultiByteToWideChar(CP_ACP, 0, input, lenA, result, lenW); } return result; } BSTR __stdcall getString() { return ANSItoBSTR("Hello world"); } 

当然,如果您使用Unicode字符串,则更容易。

 BSTR __stdcall getString() { return ::SysAllocString(L"Hello world"); } 

C#

 [DllImport(@"test.dll")] [return: MarshalAs(UnmanagedType.BStr)] private static extern string getString(); 

在C#方面就是这样。 你只需调用getString()并返回一个.net string ,你不需要编组任何东西或调用deallocator。