2009-03-26 17 views
9

I tienen un objeto COM hipotético con la siguiente firma¿C# limpia la memoria asignada C++?

void MemAlloc(ref double[] test, int membercount) 

donde la memoria se asigna en C++ usando nuevo/malloc. Una vez que esté en C#, usando el RCW, ¿cómo me aseguro de que la memoria se libera correctamente? Creo que sería difícil para .NET liberar, teniendo en cuenta que en C++ necesitas saber si se asignó con new/malloc/mm_malloc antes de poder liberarlo correctamente. Entonces, ¿cuál es la forma adecuada de limpiar mi matriz asignada de C++? Gracias.

Respuesta

8

Creo que debería usar CoTaskMemAlloc() para la memoria que desea liberar explícitamente desde el lado administrado. El CLR se ocupará de liberar la memoria una vez que ya no sea alcanzable. Si desea liberarlo explícitamente, puede usar la rutina gestionada Marshal.CoTaskFree().

En general, el contador de referencias interoperativas y CLR cumplen con las convenciones COM para liberar memoria; el destinatario es responsable de liberar la memoria. Por lo tanto, el contador de referencias de CLR/Interop normalmente se ocupará de liberar la memoria asignada en una llamada nativa si esa memoria se devuelve a la persona que realiza la llamada administrada.

De Memory Management with the Interop Marshaler (MSDN):

El contador de referencias de interoperabilidad siempre intenta para liberar memoria asignada por el código no administrado . Este comportamiento cumple con las reglas de administración de memoria COM , pero difiere de las reglas que rigen C++ nativo.

confusión puede surgir si prevé C++ comportamiento nativa (sin memoria liberación) cuando utilizando la invocación de plataforma, que libera automáticamente la memoria para punteros. Por ejemplo, llamar al siguiendo el método no administrado desde una DLL C++ no libera automáticamente ninguna memoria .

El tiempo de ejecución siempre utiliza el método CoTaskMemFree para liberar memoria. Si la memoria con la que está trabajando era no asignada con el método CoTaskMemAlloc , debe usar un IntPtr y liberar la memoria manualmente utilizando el método apropiado.

+0

gracias, eso era exactamente lo que estaba buscando – Steve

6

Envuélvala en un objeto que implemente IDisposable y asegúrese de que la envoltura C# se elimine.

Here's a blog I wrote sobre una forma fácil de implementar IDisposable.

+0

Usar IDisposable es una buena estrategia de gestión de recursos, pero ¿cómo se implementa el método Dispose para liberar realmente la memoria? –

1

estoy casi 100% seguro de que el CLR no se liberará automáticamente la memoria asignada para la prueba (si fuera PInvoke estaría seguro al 100%). La razón es que, ¿cómo sabe el CLR lo que usaste para asignar la memoria en primer lugar? Es decir, no.

Una forma más segura para escribir esta función es la siguiente

void MemAlloc(ref IntPtr arrayPtr, int membercount) 

vez que obtenga la devolución de llamada se puede hacer lo siguiente.

var count = GetTheCount(); 
var arrayPtr = IntPtr.Zero; 
obj.MemAlloc(ref arrayPtr, count); 
byte[] test = MarshalThePtrToByteArray(arrayPtr, count); 
Marshal.FreeCoTaskMem(arrayPtr); 

Aquí es una aplicación rápida y sucia de MarashalThePtrToByteArray

byte[] MarashalThePtrToByteArray(IntPtr ptr, int count) { 
    byte[] arr = new byte[count]; 
    for (int i = 0; i < count; i++) { 
    arr[i] = (byte)Marshal.PtrToStructure(ptr, typeof(byte)); 
    ptr = new IntPtr(IntPtr.ToInt64() + Marshal.SizeOf(typeof(byte))); 
    } 
    return arr; 
} 
+0

Para COM Interop, el contador de referencias CLR/Interop usa la convención COM de liberar memoria devuelta a la persona que llama, siempre que la memoria se haya asignado con CoTaskMemAlloc(). –

+0

@ Arnshea, pero ¿cómo sabe el CLR que fue asignado con CoTaskMemAlloc? No puede y muchas implementaciones COM no usan CoTaskMemAlloc para datos. – JaredPar

+0

Es cierto. En estos casos, donde está implementando el método nativo, debería usar CoTaskMemAlloc() en lugar de new/mallow(). Entonces el contador de interferencias se encargará de liberar la memoria. –

Cuestiones relacionadas