Je veux passer un byte[]à une méthode prend un IntPtrparamètre en C #, est-ce possible et comment?
GCHandleméthode fonctionne comme un charme ... aussi la fixedméthode. : P :))
Je veux passer un byte[]à une méthode prend un IntPtrparamètre en C #, est-ce possible et comment?
GCHandleméthode fonctionne comme un charme ... aussi la fixedméthode. : P :))
Réponses:
Vous n'êtes pas sûr d'obtenir un IntPtr dans un tableau, mais vous pouvez copier les données à utiliser avec du code non managé à l'aide de Mashal.Copy:
IntPtr unmanagedPointer = Marshal.AllocHGlobal(bytes.Length);
Marshal.Copy(bytes, 0, unmanagedPointer, bytes.Length);
// Call unmanaged code
Marshal.FreeHGlobal(unmanagedPointer);
Vous pouvez également déclarer une structure avec une propriété, puis utiliser Marshal.PtrToStructure, mais cela nécessiterait toujours l'allocation de mémoire non managée.
Edit: De plus, comme l'a souligné Tyalis, vous pouvez également utiliser un code fixe si un code non sécurisé est une option pour vous
Marshal.Copyavec cette surcharge, il faut un index de départ. L'appel devrait êtreMarshal.Copy(bytes, 0, unmanagedPointer, bytes.Length);
Autrement,
GCHandle pinnedArray = GCHandle.Alloc(byteArray, GCHandleType.Pinned);
IntPtr pointer = pinnedArray.AddrOfPinnedObject();
// Do your stuff...
pinnedArray.Free();
Cela devrait fonctionner mais doit être utilisé dans un contexte dangereux:
byte[] buffer = new byte[255];
fixed (byte* p = buffer)
{
IntPtr ptr = (IntPtr)p;
// do you stuff here
}
attention, il faut utiliser le pointeur dans le bloc fixe! Le gc peut déplacer l'objet une fois que vous n'êtes plus dans le bloc fixe.
Vous pouvez utiliser Marshal.UnsafeAddrOfPinnedArrayElement(array, 0)pour obtenir un pointeur de mémoire vers le tableau.
Voici une torsion sur la réponse de @ user65157 (+1 pour cela, BTW):
J'ai créé un wrapper IDisposable pour l'objet épinglé:
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();
}
}
puis utilisez-le comme ceci:
using (AutoPinner ap = new AutoPinner(MyManagedObject))
{
UnmanagedIntPtr = ap; // Use the operator to retrieve the IntPtr
//do your stuff
}
J'ai trouvé que c'était une bonne façon de ne pas oublier d'appeler Free () :)
Marshal.Copy fonctionne mais est plutôt lent. Le plus rapide est de copier les octets dans une boucle for. Encore plus rapide est de convertir le tableau d'octets en un tableau ulong, de copier autant d'ulong que tient dans le tableau d'octets, puis de copier les 7 octets restants possibles (le chemin qui n'est pas aligné sur 8 octets). Le plus rapide est d'épingler le tableau d'octets dans une instruction fixe comme proposé ci-dessus dans la réponse de 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;
}
Dans certains cas, vous pouvez utiliser un type Int32 (ou Int64) dans le cas de IntPtr. Si vous le pouvez, une autre classe utile est BitConverter. Pour ce que vous voulez, vous pouvez utiliser BitConverter.ToInt32 par exemple.
Int32comme pointeur. C'était une mauvaise pratique faite il y a des années et cela a conduit à toutes sortes de problèmes de portage. Même un Int64n'est pas sûr car il existe déjà des architectures 128 bits et la taille du pointeur augmentera. Les pointeurs ne devraient jamais être représentés que comme des pointeurs.
int/ longpour les pointeurs est lorsque le langage utilisé n'en a aucun concept (par exemple VB6). C # prend en charge les pointeurs et a IntPtr- il n'est pas du tout nécessaire d'utiliser un intà la place d'un pointeur. Je supprimerai mon -1 si vous ajoutez des avertissements clairs et une explication des problèmes potentiels à votre réponse.