Я хочу передать byte[]метод, принимающий IntPtrпараметр в C #, возможно ли это и как?
GCHandleметод работает как шарм ... также fixedметод. : P :))
Я хочу передать byte[]метод, принимающий IntPtrпараметр в C #, возможно ли это и как?
GCHandleметод работает как шарм ... также fixedметод. : P :))
Ответы:
Не уверен в получении 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);
По-другому,
GCHandle pinnedArray = GCHandle.Alloc(byteArray, GCHandleType.Pinned);
IntPtr pointer = pinnedArray.AddrOfPinnedObject();
// Do your stuff...
pinnedArray.Free();
Это должно работать, но должно использоваться в небезопасном контексте:
byte[] buffer = new byte[255];
fixed (byte* p = buffer)
{
IntPtr ptr = (IntPtr)p;
// do you stuff here
}
будьте осторожны, вы должны использовать указатель в фиксированном блоке! Gc может перемещать объект, когда вы больше не находитесь в фиксированном блоке.
Вы можете использовать Marshal.UnsafeAddrOfPinnedArrayElement(array, 0)для получения указателя памяти на массив.
Вот поворот к ответу @ 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 () :)
Marshal.Copy работает, но довольно медленно. Быстрее копировать байты в цикле for. Еще быстрее - преобразовать массив байтов в массив ulong, скопировать столько ulong, сколько поместится в массиве байтов, а затем скопировать возможные оставшиеся 7 байтов (след, который не выровнен на 8 байтов). Самый быстрый - закрепить массив байтов в фиксированном операторе, как было предложено выше в ответе Тялиса.
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;
}
В некоторых случаях вы можете использовать тип Int32 (или Int64) в случае IntPtr. Если можете, еще один полезный класс - BitConverter. Для того, что вы хотите, вы можете использовать, например, BitConverter.ToInt32.
Int32в качестве указателя. Это была плохая практика, применявшаяся много лет назад, и она приводила ко всем видам проблем с переносом. Даже Int64небезопасно, потому что уже есть 128-битные архитектуры и размер указателя увеличится. Указатели всегда должны быть представлены только как указатели.
int/ longдля указателей - это когда используемый язык не имеет их понятия (например, VB6). C # поддерживает указатели и вообще IntPtrне имеет необходимости использовать intвместо указателя. Я удалю свой -1, если вы добавите в свой ответ четкие предупреждения и объяснение потенциальных проблем.