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);