Подтвердить что ты не робот

Marshal.AllocHGlobal VS Marshal.AllocCoTaskMem, Marshal.SizeOf VS 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 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).

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

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

4b9b3361

Ответ 1

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

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

Всякий раз, когда вы пишете неуправляемый код взаимодействия, вы всегда должны избегать ситуации, когда код, который выделяет неуправляемую память, не совпадает с кодом, который освобождает его. Было бы неплохо, если используется неправильный разделитель. Это особенно верно для любого кода, который перехватывает программу 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 наиболее вероятным средством успеха.

Ответ 2

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

2) Используйте Marshal.SizeOf(typeof (WAVEHDR)). Хотя вы можете использовать метод Marshal.SizeOf, значение, возвращаемое этим методом, не всегда совпадает с значением, возвращаемым sizeof. Marshal.SizeOf возвращает размер после того, как тип был маршализирован, тогда как sizeof возвращает размер, поскольку он был выделен средой общего языка, включая любое дополнение.

Ответ 3

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

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