pinvoke: как освободить строку malloc'D?
в dll C у меня есть такая функция:
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;
}
Я хотел бы вызвать его с C#, используя P / invoke.
[DllImport("MyDll.dll")]
private static extern string GetSomeText(string strInput);
Как правильно освободить выделенную память?
Я пишу кросс-платформенный код, ориентированный как на Windows, так и на Linux.
изменить: Такой
[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);
3 ответов
вы должны маршалировать возвращенные строки как IntPtr
в противном случае среда CLR может освободить память с помощью неправильного распределителя, потенциально вызывая повреждение кучи и всевозможные проблемы.
см. этот почти (но не совсем) дублирующий вопрос PInvoke для функции C, которая возвращает char*.
В идеале ваша dll C также должна выставлять FreeText
функции для вас, чтобы использовать, когда вы хотите освободить строку. Это гарантирует, что строка будет освобождена в правильном способ (даже если dll c изменяется).
добавить другую функцию ReturnSomeText
что называет free
или все, что необходимо для освобождения памяти снова.
если вы вернетесь в память .net, выделенную вашим родным malloc, вам также придется экспортировать деаллокатор. Я не считаю это желательным действием и вместо этого предпочитаю экспортировать текст как BSTR
. Это может быть освобождено средой выполнения C#, потому что она знает, что BSTR
был выделен распределителем COM. Кодирование C# становится намного проще.
единственная морщинка-это BSTR
использует символы Юникода, а код C++ использует ANSI. Я бы обошел это, как Итак:
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);