2011-09-03 17 views
6

Quiero asignar y desasignar memoria en código no administrado (C++) y las llamamos funciones del código administrado (C#). ¿No estoy seguro de si el siguiente código es correcto sin pérdidas de memoria o no?Asignación y desasignación de memoria en código no administrado utilizando la plataforma Invoke (C#)

código 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ódigo 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; 
} 

Respuesta

4

No, esto no puede funcionar. El marshaller de Pinvoke intentará liberar la memoria de la cadena con CoTaskMemFree(). De lo contrario, no sabe que tiene una función de liberación. Eso no va a funcionar bien, no asignó la cadena con CoTaskMemAlloc. Esta será una pérdida de memoria silenciosa en XP, un bloqueo en Vista y más.

usted tiene que parar el contador de referencias de tratar de hacer bien el trabajo:

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

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

Que a su vez requiere Marshal.PtrToStringAnsi() en su código C# para la cadena Mariscal mismo desde el puntero devuelto.

+0

Hola, ¿por qué no usaste la palabra clave 'out' para el parámetro en la función freeMemory? – user186246

+0

Por qué se llama CoTaskMemFree()? ¿Qué es lo que realmente hace? – user186246

+0

Porque freeMemory no devuelve un puntero. CoTaskMemAlloc() es el asignador de memoria para COM. Si no hay forma de adivinar qué montón se usó, el marshaller adivina el montón de COM. –

0

Personalmente creo que esto se hace más fácilmente usando un BSTR y evitando así la necesidad de exportar un desglose.

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

Por supuesto, si se está trabajando con cadenas Unicode es incluso más fácil.

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

C#

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

Y en el lado C# eso es todo. Simplemente llame al getString() y devuelve un .net string y no necesita ordenar nada ni llamar a un desglose.

Cuestiones relacionadas