pinvoke: Как освободить строку malloc'd? - PullRequest
9 голосов
/ 06 сентября 2011

В C dll у меня есть такая функция:

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.

Edit: Как это

[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 ]

7 голосов
/ 06 сентября 2011

Вы должны маршалировать возвращаемые строки как IntPtr, иначе CLR может освободить память, используя неправильный распределитель, что может привести к повреждению кучи и всевозможным проблемам.

Смотрите этот почти (но не совсем) дублирующий вопрос PInvoke для функции C, которая возвращает символ *.

В идеале ваша библиотека DLL также должна предоставлять функцию FreeText, которую вы можете использовать, когда хотите освободить строку.Это обеспечивает правильное освобождение строки (даже если Cll dll изменяется).

1 голос
/ 06 сентября 2011

Если вы вернетесь к памяти .net, выделенной вашим собственным malloc, вам также придется экспортировать освобождение. Я не считаю это желательным действием и вместо этого предпочитаю экспортировать текст как BSTR. Это может быть освобождено средой выполнения C #, поскольку она знает, что BSTR был выделен распределителем COM. Кодирование на C # становится намного проще.

Единственная проблема заключается в том, что BSTR использует символы Unicode, а ваш код 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);
1 голос
/ 06 сентября 2011

Добавьте еще одну функцию ReturnSomeText, которая вызывает free или что-либо еще, что необходимо для освобождения памяти.

...