Я хочу перейти 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):
Я створив обгортку, що не використовується, для закріпленого об’єкта:
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
}
Я вважав, що це хороший спосіб не забути зателефонувати безкоштовно () :)
Маршал. Копія працює, але досить повільно. Швидше - скопіювати байти в цикл for. Ще швидше - це передати байтовий масив до масиву 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;
}
У деяких випадках для IntPtr можна використовувати тип Int32 (або Int64). Якщо можете, ще один корисний клас - BitConverter. Для того, що ви хочете, ви можете використовувати BitConverter.ToInt32, наприклад.
Int32вказівник. Це було поганою практикою, зробленою роками тому, і це призводило до різного роду переносів проблем. Навіть не Int64є безпечним, оскільки вже є 128-бітна архітектура, і розмір вказівника збільшиться. Покажчики повинні бути представлені лише як покажчики.
int/ longдля покажчиків - це коли мова, що використовується, не має їх поняття (наприклад, VB6). C # підтримує покажчики і має IntPtr- взагалі немає потреби використовувати intзамість вказівника. Я видаляю -1, якщо ви додасте чіткі застереження та пояснення можливих проблем до своєї відповіді.