Понимание различий между использованием фиксированных {}, Marshal.AllocHGlobal() и GCHandle.Alloc()

Позвольте мне начать с того, что я посмотрел и нашел описания использования фиксированных {}, Marshal.AllocHGlobal() и GCHandle.Alloc() на этом форуме и во многих ссылках в Интернете. Тем не менее, мне еще предстоит найти краткое объяснение того, когда использовать класс маршала и класс GCHandle (с использованием и без использования fixed {}).

Я использую стороннюю библиотеку .NET, которая имеет метод Readline() в классе "Буфер". В руководстве показан следующий прототип функции:

bool ReadLine (int x1, int y1, int x2, int y2, System.IntPtr bufData, out int numRead);

с описанием bufData, который гласит:... Область памяти должна иметь количество байтов, большее или равное длине строки, умноженное на значение, возвращаемое значением свойство BytesPerPixel.

Теперь в руководстве пользователя сделать показан пример доступа к буфере (который я немного изменил для своего конкретного примера):

// Create an array large enough to hold one line from buffer
int size = 640; 
byte[] dataLine = new byte[size * 2];   // 2 bytes per pixel

// Pin the array to avoid Garbage collector moving it  
GCHandle dataLineHandle = GCHandle.Alloc(dataLine, GCHandleType.Pinned); 
IntPtr dataLineAddress = dataLineHandle.AddrOfPinnedObject(); 

и я мог бы следовать приведенному выше "примеру" с помощью:

// Read one line of buffer data 
success = buffer.ReadLine(0, 0, 639, 0, dataLineAddress, out numRead); 

// Unpin the array 
dataLineHandle.Free() 

Это может быть конец истории (и мне еще предстоит проверить вышеприведенный код), но я попал в gogling класс GCHandle, который повел меня на путь совместимости .NET, pInvoke и т.д.

Итак, мои вопросы... 1) Почему я не могу использовать:

IntPtr dataLineAddress = Marshal.AllocHGlobal( size * 2 );

и передать это в ReadLine()?

2) Могу ли я использовать следующий фрагмент кода (извлеченный и измененный из примеров в Интернете):

int size = 640;
byte[] dataLine= new byte[size * 2];  // 2 bytes per pixel

// prevent garbage collector from moving buffer around in memory
fixed (byte* fixedDataLine = dataLine)
{
  // get IntPtr representing address of first buffer element
  IntPtr dataLineAddress= Marshal.UnsafeAddrOfPinnedArrayElement(fixedDataLine , 0);
  success = buffer.ReadLine(0, 0, 639, 0, dataLineAddress, out numRead);
}

Мне было бы интересно, чтобы каждый мог пролить свет на вышеупомянутые методы и указать на мои ошибки в реализации, а также указать, когда эти методы подходят. Наконец, даже если все вышеприведенные методы действительны, существует ли общий толчок в последние несколько лет к одному из подходов или к другому?

Спасибо заранее! Раздутая

Ответы

Ответ 1

Ну, альтернатива, скорее всего, тоже будет работать. Но образец Marshal.AllocHGlobal не является полным, теперь вы получили данные в неуправляемой памяти. Вам все равно нужно выполнить работу, чтобы превратить ее в управляемый объект (массив), чтобы вы могли легко получить к нему доступ, вы должны вызвать Marshal.Copy(). Неэффективен, поскольку он копирует данные дважды. И не забудьте называть маршала .FreeHGlobal().

Фиксированный образец делает то же самое, что и образец поставщика, он неявно связывает память. Неловкость здесь заключается в том, что API принимает IntPtr, а не байт *. И вы должны изменить параметры компиляции, чтобы разрешить небезопасное ключевое слово. Это не является более эффективным.

Вы не продвигаетесь вперед, делая это по-другому.