Выделение и освобождение памяти в неуправляемом коде с использованием платформы Invoke (C #) - PullRequest
6 голосов
/ 03 сентября 2011

Я хочу выделить и освободить память в неуправляемом коде (C ++), и мы называем их функциями из управляемого кода (C #).Я не уверен, подходит ли следующий код без утечек памяти или нет?

Код C #:

[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 ++:

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

Ответы [ 2 ]

4 голосов
/ 03 сентября 2011

Нет, это не может работать.Маршаллер pinvoke попытается освободить память для строки с помощью CoTaskMemFree ().В противном случае он не знает, что у вас есть функция выпуска.Это не будет работать хорошо, вы не распределили строку с CoTaskMemAlloc.Это будет тихая утечка памяти в XP, сбой в Vista и выше.

Вы должны остановить маршаллера от попыток сделать правильную работу:

[DllImport("SampleDLL.dll")]
public extern void getString(out IntPtr strptr);

[DllImport("SampleDLL.dll")]
public extern void freeMemory(IntPtr strptr);

Что тогдатребует Marshal.PtrToStringAnsi () в вашем коде C #, чтобы самостоятельно упорядочить строку из возвращенного указателя.

0 голосов
/ 03 сентября 2011

Лично я думаю, что это проще всего сделать с помощью BSTR и, таким образом, избежать необходимости экспортировать деллокатор.

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

КонечноЕсли вы работаете со строками Юникода, это еще проще.

BSTR __stdcall getString()
{
    return ::SysAllocString(L"Hello world");
}

C #

[DllImport(@"test.dll")]
[return: MarshalAs(UnmanagedType.BStr)]
private static extern string getString();

И на стороне C # это все.Вы просто звоните getString(), и он возвращает .net string, и вам не нужно ничего маршалировать или вызывать коллокатор.

...