2009-03-26 2 views
9

У меня есть гипотетический COM-объект со следующей подписьюC# очищает C++ выделенную память?

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

где память выделяется в C++ с использованием нового/таНоса. Как только это происходит на C#, используя RCW, как я могу гарантировать, что память будет освобождена правильно? Я бы подумал, что .NET будет сложно освободить, учитывая, что на C++ вам нужно знать, было ли оно выделено новым/malloc/mm_malloc, прежде чем вы сможете правильно его освободить. Итак, каков способ appopriate для очистки моего выделенного массива на C++? Благодарю.

ответ

8

Я считаю, что вы должны использовать CoTaskMemAlloc() для памяти, которую вы хотите явно освободить от управляемой стороны. CLR позаботится о том, чтобы освободить память, как только она перестанет быть доступной. Если вы хотите бесплатно его освободить, вы можете использовать управляемую процедуру Marshal.CoTaskFree().

В общем, маршалер interop и CLR придерживаются соглашений COM для освобождения памяти; получатель отвечает за освобождение памяти. Таким образом, маршалер CLR/Interop обычно будет заботиться о освобождении памяти, которая была распределена в собственном вызове, если эта память возвращается управляемому вызывающему.

От Memory Management with the Interop Marshaler (MSDN):

Interop упаковщик всегда пытается , чтобы освободить память, выделенных неуправляемого кода. Такое поведение соответствует правилам управления памятью COM , но отличается от правил, которые управляют собственным C++.

Путаница может возникнуть, если вы предполагаете родной ++ поведения C (без памяти высвобождая) при использовании платформы Invoke, , который автоматически освобождает память для указателей. Например, вызов после неуправляемого метода из библиотеки C++ не автоматически освобождает память .

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

+0

спасибо, это было именно то, что я искал – Steve

6

Оберните его в объект, который реализует IDisposable и убедитесь, что оболочка C# удалена.

Here's a blog I wrote про простой способ реализации IDisposable.

+0

Использование IDisposable - хорошая стратегия управления ресурсами, но как вы реализуете метод Dispose, чтобы фактически освободить память? –

1

Я почти на 100% уверен, что CLR автоматически не освободит память, выделенную для тестирования (если бы это был PInvoke, я был бы на 100% уверен). Причина в том, как CLR знает, что вы использовали для выделения памяти в первую очередь? А это не так.

Более безопасный способ, чтобы написать эту функцию следующим образом

void MemAlloc(ref IntPtr arrayPtr, int membercount) 

После того, как вы получите обратный звонок вы можете сделать следующее.

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

Вот быстрый и грязный реализация 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

Для COM Interop маршалер CLR/Interop использует соглашение COM о выпуске памяти, возвращенной вызывающему, до тех пор, пока память была выделена CoTaskMemAlloc(). –

+0

@ Arnshea, но как CLR знал, что он был выделен CoTaskMemAlloc? Он не может и многие COM-реализации не используют CoTaskMemAlloc для данных. – JaredPar

+0

Правда. В этом случае, когда он реализует собственный метод, он должен использовать CoTaskMemAlloc() вместо new/mallow(). Тогда посредник-посредник позаботится о освобождении памяти. –

Смежные вопросы