In a C dll, I have a function like this:
char* GetSomeText(char* szInputText)
{
char* ptrReturnValue = (char*) malloc(strlen(szInputText) * 1000); // Actually done after parsemarkup with the proper length
init_parser(); // Allocates an internal processing buffer for ParseMarkup result, which I need to copy
sprintf(ptrReturnValue, "%s", ParseMarkup(szInputText) );
terminate_parser(); // Frees the internal processing buffer
return ptrReturnValue;
}
I would like to call it from C# using P/invoke.
[DllImport("MyDll.dll")]
private static extern string GetSomeText(string strInput);
How do I properly release the allocated memory?
I am writing cross-platform code targeting both Windows and Linux.
Edit:
Like this
[DllImport("MyDll.dll")]
private static extern System.IntPtr GetSomeText(string strInput);
[DllImport("MyDll.dll")]
private static extern void FreePointer(System.IntPtr ptrInput);
IntPtr ptr = GetSomeText("SomeText");
string result = Marshal.PtrToStringAuto(ptr);
FreePointer(ptr);
You should marshal returned strings as IntPtr
otherwise the CLR may free the memory using the wrong allocator, potentially causing heap corruption and all sorts of problems.
See this almost (but not quite) duplicate question PInvoke for C function that returns char *.
Ideally your C dll should also expose a FreeText
function for you to use when you wish to free the string. This ensures that the string is deallocated in the correct way (even if the C dll changes).
Add another function ReturnSomeText
that calls free
or whatever is needed to release the memory again.
If you return to .net memory allocated with your native malloc, then you also have to export the deallocator. I don't regard that to be a desirable action and instead prefer to export the text as a BSTR
. This can be freed by the C# runtime because it knows that the BSTR
was allocated by the COM allocator. The C# coding becomes a lot simpler.
The only wrinkle is that a BSTR
uses Unicode characters and your C++ code uses ANSI. I would work around that like so:
C++
#include <comutil.h>
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 GetSomeText(char* szInputText)
{
return ANSItoBSTR(szInputText);
}
C#
[DllImport("MyDll.dll", CallingConvention=CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.BStr)]
private static extern string GetSomeText(string strInput);