Voglio passare un byte[]metodo a prende un IntPtrparametro in C #, è possibile e come?
GCHandlemetodo funziona come un incantesimo ... anche il fixedmetodo. : P :))
Voglio passare un byte[]metodo a prende un IntPtrparametro in C #, è possibile e come?
GCHandlemetodo funziona come un incantesimo ... anche il fixedmetodo. : P :))
Risposte:
Non sei sicuro di ottenere un IntPtr in un array, ma puoi copiare i dati per utilizzarli con codice non gestito usando Mashal.Copy:
IntPtr unmanagedPointer = Marshal.AllocHGlobal(bytes.Length);
Marshal.Copy(bytes, 0, unmanagedPointer, bytes.Length);
// Call unmanaged code
Marshal.FreeHGlobal(unmanagedPointer);
In alternativa, è possibile dichiarare una struttura con una proprietà e quindi utilizzare Marshal.PtrToStructure, ma ciò richiederebbe comunque l'allocazione della memoria non gestita.
Modifica: Inoltre, come sottolineato da Tyalis, puoi anche usare fisso se il codice non sicuro è un'opzione per te
Marshal.Copycon quel sovraccarico occorre un indice iniziale. La chiamata dovrebbe essereMarshal.Copy(bytes, 0, unmanagedPointer, bytes.Length);
Un altro modo,
GCHandle pinnedArray = GCHandle.Alloc(byteArray, GCHandleType.Pinned);
IntPtr pointer = pinnedArray.AddrOfPinnedObject();
// Do your stuff...
pinnedArray.Free();
Questo dovrebbe funzionare ma deve essere utilizzato in un contesto non sicuro:
byte[] buffer = new byte[255];
fixed (byte* p = buffer)
{
IntPtr ptr = (IntPtr)p;
// do you stuff here
}
attenzione, devi usare il puntatore nel blocco fisso! Il gc può spostare l'oggetto una volta che non sei più nel blocco fisso.
È possibile utilizzare Marshal.UnsafeAddrOfPinnedArrayElement(array, 0)per ottenere un puntatore di memoria all'array.
Ecco una svolta alla risposta di @ user65157 (+1 per quello, a proposito):
Ho creato un wrapper IDisposable per l'oggetto appuntato:
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();
}
}
quindi usalo così:
using (AutoPinner ap = new AutoPinner(MyManagedObject))
{
UnmanagedIntPtr = ap; // Use the operator to retrieve the IntPtr
//do your stuff
}
Ho trovato che questo è un bel modo di non dimenticare di chiamare Free () :)
Marshal.Copy funziona ma è piuttosto lento. Più veloce è copiare i byte in un ciclo for. Ancora più veloce è lanciare l'array di byte in un array ulong, copiare quanto più ultrale si adatta all'array di byte, quindi copiare i possibili 7 byte rimanenti (la traccia che non è allineata di 8 byte). Il più veloce è bloccare l'array di byte in un'istruzione fissa come proposto sopra nella risposta di Tyalis.
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;
}
In alcuni casi è possibile utilizzare un tipo Int32 (o Int64) nel caso di IntPtr. Se puoi, un'altra classe utile è BitConverter. Per quello che vuoi puoi usare BitConverter.ToInt32 per esempio.
Int32un puntatore. Questa è stata una cattiva pratica fatta anni fa e ha portato a tutti i tipi di problemi di porting. Anche un Int64non è sicuro perché ci sono già architetture a 128 bit e la dimensione del puntatore aumenterà. I puntatori dovrebbero essere sempre e solo rappresentati come puntatori.
int/ longper i puntatori è quando la lingua utilizzata non ne ha alcun concetto (ad esempio VB6). C # supporta i puntatori e ha IntPtr- non c'è mai bisogno di usare intmai al posto di un puntatore. Rimuoverò il mio -1 se aggiungi avvisi chiari e una spiegazione di potenziali problemi alla tua risposta.