I want to allocate and deallocate memory in unmanaged code (C++) and we call them functions from managed code (C#). Iam not sure whether the开发者_如何学Go following code is fine without memory leaks or not?
C# code:
[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++ code:
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;
}
No, this cannot work. The pinvoke marshaller is going to try to release the memory for the string with CoTaskMemFree(). It doesn't otherwise know that you have a release function. That's not going to work well, you didn't allocate the string with CoTaskMemAlloc. This is going to be a silent memory leak in XP, a crash in Vista and up.
You have to stop the marshaller from trying to do the right job:
[DllImport("SampleDLL.dll")]
public extern void getString(out IntPtr strptr);
[DllImport("SampleDLL.dll")]
public extern void freeMemory(IntPtr strptr);
Which then requires Marshal.PtrToStringAnsi() in your C# code to marshal the string yourself from the returned pointer.
Personally I think this is most easily done using a BSTR
and thus avoiding the need to export a deallocator.
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");
}
Of course, if you are working with Unicode strings its even easier.
BSTR __stdcall getString()
{
return ::SysAllocString(L"Hello world");
}
C#
[DllImport(@"test.dll")]
[return: MarshalAs(UnmanagedType.BStr)]
private static extern string getString();
And on the C# side that's it. You just call getString()
and it returns a .net string
and you don't need to marshall anything or call a deallocator.
精彩评论