Chcę przekazać byte[]do metody przyjmuje IntPtrparametr w języku C #, czy jest to możliwe i jak?
GCHandlemetoda działa jak urok ... także fixedmetoda. : P :))
Chcę przekazać byte[]do metody przyjmuje IntPtrparametr w języku C #, czy jest to możliwe i jak?
GCHandlemetoda działa jak urok ... także fixedmetoda. : P :))
Odpowiedzi:
Nie mam pewności co do pobrania IntPtr do tablicy, ale możesz skopiować dane do użycia z niezarządzanym kodem za pomocą Mashal.Copy:
IntPtr unmanagedPointer = Marshal.AllocHGlobal(bytes.Length);
Marshal.Copy(bytes, 0, unmanagedPointer, bytes.Length);
// Call unmanaged code
Marshal.FreeHGlobal(unmanagedPointer);
Alternatywnie możesz zadeklarować strukturę z jedną właściwością, a następnie użyć Marshal.PtrToStructure, ale nadal wymagałoby to przydzielenia niezarządzanej pamięci.
Edycja: Ponadto, jak wskazał Tyalis, możesz również użyć opcji naprawionej, jeśli niebezpieczny kod jest dla ciebie opcją
Marshal.Copyprzy tym przeciążeniu potrzebny jest indeks początkowy. Wezwanie powinno byćMarshal.Copy(bytes, 0, unmanagedPointer, bytes.Length);
Inny sposób,
GCHandle pinnedArray = GCHandle.Alloc(byteArray, GCHandleType.Pinned);
IntPtr pointer = pinnedArray.AddrOfPinnedObject();
// Do your stuff...
pinnedArray.Free();
To powinno działać, ale musi być używane w niebezpiecznym kontekście:
byte[] buffer = new byte[255];
fixed (byte* p = buffer)
{
IntPtr ptr = (IntPtr)p;
// do you stuff here
}
uważaj, musisz użyć wskaźnika w ustalonym bloku! Gc może przesuwać obiekt, gdy nie jesteś już w ustalonym bloku.
Możesz użyć, Marshal.UnsafeAddrOfPinnedArrayElement(array, 0)aby uzyskać wskaźnik pamięci do tablicy.
Oto zwrot w odpowiedzi @ user65157 (przy okazji +1, BTW):
Utworzyłem opakowanie IDisposable dla przypiętego obiektu:
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();
}
}
następnie użyj go w ten sposób:
using (AutoPinner ap = new AutoPinner(MyManagedObject))
{
UnmanagedIntPtr = ap; // Use the operator to retrieve the IntPtr
//do your stuff
}
Uważam, że to miły sposób, aby nie zapomnieć zadzwonić do Free () :)
Marshal.Copy działa, ale działa dość wolno. Szybsze jest skopiowanie bajtów w pętli for. Jeszcze szybsze jest rzutowanie tablicy bajtów na tablicę ulong, skopiowanie tyle ulong, ile mieści się w tablicy bajtów, a następnie skopiowanie możliwych pozostałych 7 bajtów (ślad, który nie jest wyrównany 8 bajtów). Najszybciej jest przypięcie tablicy bajtów w ustalonej instrukcji, jak zaproponowano powyżej w odpowiedzi Tyalisa.
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;
}
W niektórych przypadkach można użyć typu Int32 (lub Int64) w przypadku IntPtr. Jeśli możesz, inną przydatną klasą jest BitConverter. Do tego, co chcesz, możesz na przykład użyć BitConverter.ToInt32.
Int32jako wskaźnika. To była zła praktyka zrobiona lata temu i doprowadziła do różnego rodzaju problemów z portowaniem. Nawet Int64nie jest bezpieczny, ponieważ istnieją już architektury 128-bitowe, a rozmiar wskaźnika wzrośnie. Wskaźniki powinny być zawsze przedstawiane tylko jako wskaźniki.
int/ longdla wskaźników jest sytuacja, gdy używany język nie ma ich koncepcji (na przykład VB6). C # obsługuje wskaźniki i ma IntPtr- nie ma potrzeby używania intkiedykolwiek wskaźnika zamiast wskaźnika. Usunę moje -1, jeśli dodasz jasne ostrzeżenia i wyjaśnienie potencjalnych problemów do odpowiedzi.