Pouvez-vous fournir plus de détails? Pourquoi voudriez-vous le faire?
Grzenio
2
Vous en avez besoin si vous utilisez l'API DirectShow par exemple ... pour obtenir des données de VideoRenderer, vous devez utiliser ceci ... et la GCHandleméthode fonctionne comme un charme ... aussi la fixedméthode. : P :))
Cipi
Vous en avez besoin pour tout ce qui transfère des terrabytes de données et vous voulez éviter la copie supplémentaire. Utilise ton imagination.
Brain2000
Réponses:
93
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:
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
Juste pour clarifier, Marshal.Copyavec cette surcharge, il faut un index de départ. L'appel devrait êtreMarshal.Copy(bytes, 0, unmanagedPointer, bytes.Length);
mkenyon
mieux pour obtenir IntPtr sans créer de nouvelle mémoire, comme la réponse de @ user65157.
Lin
208
Autrement,
GCHandle pinnedArray =GCHandle.Alloc(byteArray,GCHandleType.Pinned);IntPtr pointer = pinnedArray.AddrOfPinnedObject();// Do your stuff...
pinnedArray.Free();
@Cipi Selon l'un des autres articles d'Eric Lipperts, cela devrait avoir le mot clé Fixed au lieu d'utiliser le GC
goodguys_activate
Merci beaucoup. Travaillez bien et c'est assez facile. Je ne suis pas aussi doué pour GCHandles et Marshal. Quelqu'un peut-il me dire quels avantages ont le maréchal et ce que le GCHandle et où utiliser lequel? Merci
piggy
1
Quelqu'un pourrait-il s'il vous plaît fournir la référence au message d'Eric Lippert?
Cameron
3
@piggy: Je pense que l'inconvénient de Marshal est que vous devez faire une copie de vos données (ce qui peut prendre beaucoup de temps et gaspiller la mémoire dont vous pourriez avoir besoin)
Riki
6
@ makerofthings7 Je ne crois pas que Lippert dise d'utiliser «fixe» au lieu du GC [pour épingler des objets], je crois qu'il dit de ne pas utiliser le GC pour épingler un objet indéfiniment, tout en disant de ne pas utiliser fixe pour faire le pareil non plus.
Cameron
129
Cela devrait fonctionner mais doit être utilisé dans un contexte dangereux:
byte[] buffer =newbyte[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 voudrez peut-être étudier la dérivation de votre AutoPinner à partir de SafeHandle car cette classe prend en compte les «pièges» de la concurrence et de la sécurité, ainsi que l'encouragement / l'utilisation du modèle IDisposable recommandé.
kkahl
0
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.
IntPtrGetIntPtr(Byte[] byteBuf){IntPtr ptr =Marshal.AllocHGlobal(byteBuf.Length);for(int i =0; i < byteBuf.Length; i++){Marshal.WriteByte(ptr, i, byteBuf[i]);}return ptr;}
Ceci est une copie plus inefficace de la réponse acceptée. Pourquoi copieriez-vous octet par octet au lieu de tous en même temps?
BDL le
J'ai trouvé l'erreur dans la réponse acceptée dans mon code. J'ai appelé la fonction dll C ++ en c #. char * paramètre utilisé en c ++, j'ai donc utilisé la méthode acceptée. [DllImport ("MyDll.dll", EntryPoint = "functionName", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] mais étrange, lors de l'utilisation de la méthode acceptée, je ne peux pas obtenir le bon IntPtr, j'ai un tampon cassé , une partie du tampon était affichée en Unicode. j'ai donc utilisé cette méthode
nexdev le
-6
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.
Vous ne devez jamais, jamais utiliser Int32 ou Int64 à la place d'un pointeur . Si vous devez porter votre code sur une plate-forme différente (32 bits -> 64 bits), vous aurez toutes sortes de maux de tête.
xxbbcc
5
Non, il n'y a pas de cas valide dans lequel vous pouvez utiliser correctement et en toute sécurité un 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.
xxbbcc
1
Je l'ai utilisé dans des projets .NET CF sans problème, bien sûr, vous auriez des problèmes si vous essayez de le porter sur d'autres systèmes, mais il existe du code qui n'est pas destiné à être porté.
Alejandro Mezcua
Il est vrai que certains codes ne sont pas prévus pour être portés mais les choses peuvent changer assez rapidement. Même si votre utilisation sur le CF était justifiée (je ne sais pas), c'est quand même un mauvais conseil à une question générique. Le seul scénario valable pour utiliser 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.
xxbbcc
3
Eh bien, vous êtes comparez l'utilisation du code très spécifique marshalling à cette question générale sur l' utilisation de pointeurs en C #. En C # normal, il n'y a pas de scénario où cela serait valide. Je sais que c'est une vieille question, mais je l'ai rejetée parce que je suis tombée dessus en cherchant le moyen d'allouer de la mémoire pour un IntPtr - d'autres le verront aussi. Je vois votre conseil comme très dangereux parce que les gens vont l'accepter en pensant qu'ils ont réussi à résoudre un problème facilement alors que tout ce qu'ils ont, ce sont des problèmes futurs.
GCHandle
méthode fonctionne comme un charme ... aussi lafixed
mé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:
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
la source
Marshal.Copy
avec cette surcharge, il faut un index de départ. L'appel devrait êtreMarshal.Copy(bytes, 0, unmanagedPointer, bytes.Length);
Autrement,
la source
Cela devrait fonctionner mais doit être utilisé dans un contexte dangereux:
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.
la source
Vous pouvez utiliser
Marshal.UnsafeAddrOfPinnedArrayElement(array, 0)
pour obtenir un pointeur de mémoire vers le tableau.la source
Voici une torsion sur la réponse de @ user65157 (+1 pour cela, BTW):
J'ai créé un wrapper IDisposable pour l'objet épinglé:
puis utilisez-le comme ceci:
J'ai trouvé que c'était une bonne façon de ne pas oublier d'appeler Free () :)
la source
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.
la source
la source
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.
la source
Int32
comme 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 unInt64
n'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
/long
pour les pointeurs est lorsque le langage utilisé n'en a aucun concept (par exemple VB6). C # prend en charge les pointeurs et aIntPtr
- il n'est pas du tout nécessaire d'utiliser unint
à 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.