2009-03-26 3 views
9

Ich habe ein hypothetische COM-Objekt mit der folgenden SignaturBereinigt C# C++ reservierten Speicher?

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

wo der Speicher in C++ zugeordnet ist neu/malloc verwenden. Sobald dies in C# ist, mithilfe von RCW, wie stelle ich sicher, dass der Speicher ordnungsgemäß freigegeben wird? Ich würde denken, es wäre schwierig für .NET zu befreien, wenn man bedenkt, dass man in C++ wissen muss, ob es mit dem neuen/malloc/mm_malloc zugewiesen wurde, bevor man es korrekt freigeben kann. Also, was ist die geeignete Methode, um mein C++ zugewiesenes Array zu bereinigen? Vielen Dank.

Antwort

8

Ich glaube, dass Sie CoTaskMemAlloc() für Arbeitsspeicher verwenden sollten, die Sie explizit von der verwalteten Seite freigeben möchten. Die CLR wird dafür sorgen, dass der Speicher freigegeben wird, sobald er nicht mehr erreichbar ist. Wenn Sie es explizit freigeben möchten, können Sie die verwaltete Marshal.CoTaskFree() Routine verwenden.

Im Allgemeinen halten der Interop-Marshaler und CLR die COM-Konventionen ein, um Speicher freizugeben; Der Empfänger ist verantwortlich für die Freigabe von Speicher. Daher sorgt der CLR/Interop-Marshaller normalerweise dafür, dass Speicher freigegeben wird, der in einem systemeigenen Anruf zugewiesen wurde, wenn dieser Speicher an den verwalteten Anrufer zurückgegeben wird.

Von Memory Management with the Interop Marshaler (MSDN):

Die Interop-Marshaller versuchen immer zu frei von nicht verwalteten Code zugewiesenen Speicher. Dieses Verhalten entspricht COM Speicherverwaltungsregeln, unterscheidet sich jedoch von den Regeln, die natives C++ steuern.

Verwirrung kann entstehen, wenn Sie native C++ Verhalten (kein Memory- befreiend) antizipieren, wenn Plattformaufruf verwenden, die automatisch Speicher für Zeiger befreit. Wenn Sie beispielsweise die folgende unmanaged Methode von einer C++ DLL aufrufen, wird automatisch kein Speicher freigegeben.

Die Laufzeit verwendet immer die CoTaskMemFree-Methode, um Speicher freizugeben. Wenn der Speicher, mit dem Sie arbeiten, nicht mit der Methode CoTaskMemAlloc zugewiesen wurde, müssen Sie einen IntPtr verwenden und den Speicher manuell freigeben, indem Sie die entsprechende Methode verwenden.

+0

Danke, das war genau das, was ich suchte – Steve

6

Wickeln Sie es in ein Objekt ein, das IDisposable implementiert, und stellen Sie sicher, dass der C# -Wrapper entsorgt wird.

Here's a blog I wrote über eine einfache Möglichkeit, IDisposable zu implementieren.

+0

Die Verwendung von IDisposable ist eine gute Ressourcenverwaltungsstrategie, aber wie implementieren Sie die Dispose-Methode, um den Speicher tatsächlich freizugeben? –

1

Ich bin fast 100% sicher, dass die CLR wird nicht automatisch die für den Test zugewiesenen Speicher freizugeben (wenn es PInvoke wäre, würde ich 100% sicher sein). Der Grund dafür ist, wie weiß die CLR, was Sie verwendet haben, um den Speicher überhaupt zu reservieren? Nämlich nicht.

Ein sicherer Weg, um diese Funktion zu schreiben ist wie

void MemAlloc(ref IntPtr arrayPtr, int membercount) 

folgt Sobald Sie den Anruf zurückbekommen können Sie die folgenden Aktionen aus.

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

Hier ist eine schnelle und schmutzige Implementierung von 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

Für COM Interop der CLR/Interop Marshaller verwendet die COM-Konvention der Freigabe von Speicher an den Aufrufer zurückgegeben, solange der Speicher mit CoTaskMemAlloc() zugeordnet wurde. –

+0

@Arnshea, aber woher weiß die CLR, dass sie mit CoTaskMemAlloc zugewiesen wurde? Es kann nicht und viele COM-Implementierungen verwenden CoTaskMemAlloc für Daten nicht. – JaredPar

+0

Wahr. In diesen Fällen, in denen er die native Methode implementiert, sollte er CoTaskMemAlloc() anstelle von new/malow() verwenden. Dann wird der Interop Marshaler dafür sorgen, dass die Erinnerung frei wird. –