2009-12-11 2 views
22

У меня следующая структура:Marshal.AllocHGlobal В.С. Marshal.AllocCoTaskMem, Marshal.SizeOf В.С. SizeOf()

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] 
public struct WAVEHDR 
{ 
    internal IntPtr lpData; // pointer to locked data buffer 
    internal uint dwBufferLength; // length of data buffer 
    internal uint dwBytesRecorded; // used for input only 
    internal IntPtr dwUser; // for client's use 
    internal uint dwFlags; // assorted flags (see defines) 
    internal uint dwLoops; // loop control counter 
    internal IntPtr lpNext; // reserved for driver 
    internal IntPtr reserved; // reserved for driver 
} 

мне нужно выделить неуправляемые памяти для хранения экземпляра выше структуры. Указатель на эту структуру будет передан функции waveOut win32 api (waveOutPrepareHeader, waveOutWrite, waveOutUnprepareHeader).

  1. Должен ли я использовать Marshal.AllocHGlobal() или Marshal.AllocCoTaskMem()? В чем разница?
  2. Должен ли я передать sizeof(WAVEHDR) или Marshal.SizeOf(typeof(WAVEHDR)) методу выделения памяти? В чем разница?

УКАЗАНИЕ, что выделенная память должна быть закреплена.

ответ

38

В программе Windows всегда есть как минимум две кучи, в которых выделена неуправляемая память. Во-первых, это куча процесса по умолчанию, используемая Windows, когда ему требуется выделить память от имени программы. Вторая - куча, используемая инфраструктурой COM для распределения. Маршаллер .NET P/Invoke предполагает, что эта куча использовалась любым неуправляемым кодом, чья подпись функции требует де-выделения памяти.

AllocHGlobal выделяет из кучи процесса, AllocCoTaskMem выделяет из кучи COM.

Всякий раз, когда вы пишете неуправляемый код взаимодействия, вы всегда должны избегать ситуации, когда код, который выделяет неуправляемую память, не совпадает с кодом, который освобождает его. Было бы неплохо, если используется неправильный разделитель. Это особенно верно для любого кода, который перехватывает программу C/C++. Такие программы имеют свой собственный распределитель, который использует свою собственную кучу, созданную ЭЛТ при запуске. Отмена выделения такой памяти в другом коде невозможна, вы не можете надежно получить дескриптор кучи. Это очень распространенный источник проблем P/Invoke, особенно потому, что функция HeapFree() в XP и ранее молча игнорирует запросы на бесплатную память, которая не была выделена в правой куче (утечка выделенной памяти), но Vista и Win7 с исключением.

Не беспокойтесь об этом в вашем случае, функции API mmsystem, которые вы используете, чисты. Они были разработаны для обеспечения того же кода, который выделяет также освобождает. Это одна из причин, по которым вы должны вызвать waveInPrepareHeader(), она выделяет буферы с тем же кодом, который в конечном итоге освобождает их. Возможно, с кучей процесса по умолчанию.

Вам нужно всего лишь выделить структуру WAVEHDR. И вы несете ответственность за его освобождение, когда вы закончите с этим. API-интерфейсы mmsystem не делают этого для вас, прежде всего потому, что они не могут сделать это надежно. Соответственно, вы можете использовать либо распределитель, вам просто нужно обязательно вызвать соответствующий бесплатный метод. Все Windows API работают таким образом. Я использую CoTaskMemAlloc(), но на самом деле это не так. Просто, если я называю плохо разработанный код, немного лучше использовать кучу COM.

Вы никогда не должны использовать sizeof() в сценарии взаимодействия. Он возвращает управляемый размер типа значения. Возможно, это не так, если маршаллер P/Invoke перевел тип структуры в соответствии с директивами [StructLayout] и [MarshalAs]. Только Marshal.SizeOf() дает вам гарантированное правильное значение.


ОБНОВЛЕНИЕ: в VS2012 произошли большие изменения. Библиотека времени выполнения C, включенная в нее, теперь выделяет из кучи процесса по умолчанию вместо использования своей собственной кучи. Долгосрочный, что делает AllocHGlobal наиболее вероятным средством для успеха.

+1

Существуют ли различия в производительности между двумя функциями распределения? – DxCK

+3

'AllocCoTaskMem' более совершенен. 'AllocHGlobal' вызывает' LocalAlloc', который имеет следующее примечание: «Локальные функции имеют большие накладные расходы и предоставляют меньше функций, чем другие функции управления памятью». См. Https://msdn.microsoft.com/en-us/library/windows/desktop/aa366723(v=vs.85).aspx – IamIC

2

1) Marshal.AllocHGlobal будет работать точно. Основываясь на документах для Marshal.AllocCoTaskMem, Marshal.AllocCoTaskMem тоже должен работать.

2) Использовать маршал.SizeOf (typeof (WAVEHDR)). Although you can use the Marshal.SizeOf method, the value returned by this method is not always the same as the value returned by sizeof. Marshal.SizeOf returns the size after the type has been marshaled, whereas sizeof returns the size as it has been allocated by the common language runtime, including any padding.

1

2) Насколько я знаю, sizeof могут быть использованы только с типами, которые имеют предопределенный размер в время компиляции.

Поэтому используйте Marshal.SizeOf(typeof(WAVEHDR)).

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