Как получить IntPtr из byte [] в C #


127

Я хочу передать byte[]метод, принимающий IntPtrпараметр в C #, возможно ли это и как?


Не могли бы вы предоставить более подробную информацию? Зачем тебе это нужно?
— Grzenio

2
Вам это понадобится, если вы, например, используете DirectShow API ... чтобы получить данные из VideoRenderer, вы должны использовать это ... и GCHandleметод работает как шарм ... также fixedметод. : P :))
— Cipi

Это необходимо для всего, что передает террабайты данных, и вы хотите избежать лишней копии. Использовать свое воображение.
— Brain2000

Ответы:


93

Не уверен в получении IntPtr в массив, но вы можете скопировать данные для использования с неуправляемым кодом с помощью Mashal.Copy:

IntPtr unmanagedPointer = Marshal.AllocHGlobal(bytes.Length);
Marshal.Copy(bytes, 0, unmanagedPointer, bytes.Length);
// Call unmanaged code
Marshal.FreeHGlobal(unmanagedPointer);

В качестве альтернативы вы можете объявить структуру с одним свойством, а затем использовать Marshal.PtrToStructure, но это все равно потребует выделения неуправляемой памяти.

Изменить: Кроме того, как указал Тялис, вы также можете использовать исправленный, если небезопасный код является вариантом для вас


Чтобы уточнить, для Marshal.Copyэтой перегрузки нужен начальный индекс. Звонок должен бытьMarshal.Copy(bytes, 0, unmanagedPointer, bytes.Length);
— mkenyon

лучше получить IntPtr без создания новой памяти, например, @ user65157 answer.
— Линь

208

По-другому,

GCHandle pinnedArray = GCHandle.Alloc(byteArray, GCHandleType.Pinned);
IntPtr pointer = pinnedArray.AddrOfPinnedObject();
// Do your stuff...
pinnedArray.Free();

1
@Cipi Согласно одному из других постов Эрика Липпертса, здесь должно быть ключевое слово Fixed вместо использования GC
— goodguys_activate

большое спасибо. Работать красиво и довольно легко. Я не очень разбираюсь в GCHandles и Marshal. Может кто подскажет, какие плюсы у Маршала и что за GCHandle и где какой использовать? Спасибо
— piggy

1
Не мог бы кто-нибудь дать ссылку на сообщение Эрика Липперта?
— Кэмерон

3
@piggy: Я думаю, что недостатком Marshal является то, что вам нужно делать копию ваших данных (что может занять много времени и тратить лишнюю память, которая может вам понадобиться)
— Riki

6
@ makerofthings7 Я не верю, что Липперт говорит об использовании 'fixed' вместо GC [для закрепления объектов], я полагаю, он говорит, что не используйте GC для закрепления объекта на неопределенный срок, а также говорит, что не используйте фиксированный для выполнения то же самое, либо.
— Кэмерон

129

Это должно работать, но должно использоваться в небезопасном контексте:

byte[] buffer = new byte[255];
fixed (byte* p = buffer)
{
    IntPtr ptr = (IntPtr)p;
    // do you stuff here
}

будьте осторожны, вы должны использовать указатель в фиксированном блоке! Gc может перемещать объект, когда вы больше не находитесь в фиксированном блоке.


16
Мне нравится этот ответ, потому что он не требует выделения дополнительной памяти только для доступа к данным
— Xcalibur

3
Это лучший ответ, если используется большой байт []. У копии слишком много накладных расходов
— goodguys_activate

19

Вы можете использовать Marshal.UnsafeAddrOfPinnedArrayElement(array, 0)для получения указателя памяти на массив.


1
Я думаю, что это единственный способ получить IntPtr для элемента массива, который не является 0-м (без использования небезопасного кода).
— Guido Domenici

5
Чтобы сделать этот код надежным, сначала необходимо закрепить
— sergtk

13

Вот поворот к ответу @ user65157 (+1 за это, BTW):

Я создал оболочку IDisposable для закрепленного объекта:

class AutoPinner : IDisposable
{
   GCHandle _pinnedArray;
   public AutoPinner(Object obj)
   {
      _pinnedArray = GCHandle.Alloc(obj, GCHandleType.Pinned);
   }
   public static implicit operator IntPtr(AutoPinner ap)
   {
      return ap._pinnedArray.AddrOfPinnedObject(); 
   }
   public void Dispose()
   {
      _pinnedArray.Free();
   }
}

затем используйте его так:

using (AutoPinner ap = new AutoPinner(MyManagedObject))
{
   UnmanagedIntPtr = ap;  // Use the operator to retrieve the IntPtr
   //do your stuff
}

Я обнаружил, что это хороший способ не забыть вызвать Free () :)


4
Возможно, вы захотите исследовать получение вашего AutoPinner из SafeHandle, поскольку этот класс учитывает параллелизм и безопасность, а также поощряет / использует рекомендуемый шаблон IDisposable.
— kkahl 06

0

Marshal.Copy работает, но довольно медленно. Быстрее копировать байты в цикле for. Еще быстрее - преобразовать массив байтов в массив ulong, скопировать столько ulong, сколько поместится в массиве байтов, а затем скопировать возможные оставшиеся 7 байтов (след, который не выровнен на 8 байтов). Самый быстрый - закрепить массив байтов в фиксированном операторе, как было предложено выше в ответе Тялиса.


-1
IntPtr GetIntPtr(Byte[] byteBuf)
{
    IntPtr ptr = Marshal.AllocHGlobal(byteBuf.Length);
    for (int i = 0; i < byteBuf.Length; i++)
    {
       Marshal.WriteByte(ptr, i, byteBuf[i]);
    }
    return ptr;
}

Это более неэффективный дубликат принятого ответа. Зачем копировать побайтово, а не все сразу?
— BDL,

Я обнаружил ошибку в принятом ответе в своем коде. Я вызвал функцию DLL C ++ в C #. Параметр char * используется в C ++, поэтому я использовал принятый метод. [DllImport ("MyDll.dll", EntryPoint = "functionName", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] но странно, при использовании принятого метода не удается получить правильный IntPtr, у меня был сломан некоторый буфер , какой-то буфер отображался как Unicode. поэтому я использовал этот метод
— nexdev

-6

В некоторых случаях вы можете использовать тип Int32 (или Int64) в случае IntPtr. Если можете, еще один полезный класс - BitConverter. Для того, что вы хотите, вы можете использовать, например, BitConverter.ToInt32.


14
Вы никогда не должны использовать Int32 или Int64 вместо указателя . Если вам нужно перенести код на другую платформу (32-> 64-разрядную), у вас будут всевозможные головные боли.
— xxbbcc

5
Нет, не существует допустимого случая, в котором вы могли бы правильно и безопасно использовать Int32в качестве указателя. Это была плохая практика, применявшаяся много лет назад, и она приводила ко всем видам проблем с переносом. Даже Int64небезопасно, потому что уже есть 128-битные архитектуры и размер указателя увеличится. Указатели всегда должны быть представлены только как указатели.
— xxbbcc 08

1
Я использовал его в проектах .NET CF без проблем, конечно, у вас возникнут проблемы, если вы попытаетесь перенести его на другие системы, но есть код, который не предназначен для того, чтобы когда-либо быть портированным.
— Алехандро Мескуа

Это правда, что некоторый код не планируется переносить, но все может измениться довольно быстро. Даже если ваше использование CF было оправдано (я не знаю), это все равно плохой совет на общий вопрос. Единственный допустимый сценарий для использования указателей int/ longдля указателей - это когда используемый язык не имеет их понятия (например, VB6). C # поддерживает указатели и вообще IntPtrне имеет необходимости использовать intвместо указателя. Я удалю свой -1, если вы добавите в свой ответ четкие предупреждения и объяснение потенциальных проблем.
— xxbbcc

3
Ну, будут сравнивать использование очень конкретного кода сортировочного этот общий вопрос об использовании указателей в C #. В обычном C # нет сценария, где это было бы допустимым. Я знаю, что это старый вопрос, но я отклонил его, потому что я столкнулся с ним, ища способ выделить память для IntPtr - другие тоже это увидят. Я считаю ваш совет очень опасным, потому что люди будут следовать ему, думая, что им легко удалось решить проблему, когда все, что у них есть, - это будущие неприятности.
— xxbbcc
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.