Könnten Sie weitere Details angeben? Warum willst du das tun?
Grzenio
2
Sie benötigen dies, wenn Sie zum Beispiel die DirectShow-API verwenden ... um Daten von VideoRenderer abzurufen, müssen Sie dies verwenden ... und die GCHandleMethode funktioniert wie ein Zauber ... auch die fixedMethode. : P :))
Cipi
Sie benötigen dies für alles, was Terrabyte an Daten überträgt, und Sie möchten die zusätzliche Kopie vermeiden. Nutze deine Vorstellungskraft.
Brain2000
Antworten:
93
Sie sind sich nicht sicher, ob Sie ein IntPtr in ein Array einbinden möchten, aber Sie können die Daten zur Verwendung mit nicht verwaltetem Code mithilfe von Mashal.Copy kopieren:
Alternativ könnten Sie eine Struktur mit einer Eigenschaft deklarieren und dann Marshal.PtrToStructure verwenden. Dies würde jedoch weiterhin die Zuweisung von nicht verwaltetem Speicher erfordern.
Bearbeiten: Wie Tyalis betonte, können Sie auch festen Code verwenden, wenn unsicherer Code für Sie eine Option ist
Nur um zu verdeutlichen, Marshal.Copybenötigt diese Überlastung einen Startindex. Der Anruf sollte seinMarshal.Copy(bytes, 0, unmanagedPointer, bytes.Length);
mkenyon
Es ist besser, IntPtr zu erhalten, ohne neuen Speicher zu erstellen, wie die Antwort von @ user65157.
Lin
208
Ein anderer Weg,
GCHandle pinnedArray =GCHandle.Alloc(byteArray,GCHandleType.Pinned);IntPtr pointer = pinnedArray.AddrOfPinnedObject();// Do your stuff...
pinnedArray.Free();
@Cipi Laut einem anderen Beitrag von Eric Lipperts sollte dies das Schlüsselwort Fixed haben, anstatt den GC zu verwenden
goodguys_activate
Vielen Dank. Arbeite gut und es ist ziemlich einfach. Ich bin nicht so geschickt in GCHandles und Marschall. Kann mir jemand sagen, welche Pluspunkte der Marschall und was der GCHandle hat und wo welche verwendet werden? Danke
Schweinchen
1
Könnte jemand bitte den Verweis auf den Eric Lippert-Beitrag geben?
Cameron
3
@piggy: Ich denke, der Nachteil von Marshal ist, dass Sie eine Kopie Ihrer Daten erstellen müssen (was lange dauern und Speicherplatz verschwenden kann, den Sie möglicherweise benötigen)
Riki
6
@ makerofthings7 Ich glaube nicht, dass Lippert sagt, dass er 'fest' anstelle des GC verwenden soll [um Objekte zu pinnen]. Ich glaube, er sagt, dass er den GC nicht verwenden soll, um ein Objekt auf unbestimmte Zeit zu pinnen, zusammen mit der Aussage, dass er nicht fest verwendet, um das zu tun das gleiche auch.
Cameron
129
Dies sollte funktionieren, muss jedoch in einem unsicheren Kontext verwendet werden:
byte[] buffer =newbyte[255];fixed(byte* p = buffer){IntPtr ptr =(IntPtr)p;// do you stuff here}
Vorsicht, Sie müssen den Zeiger im festen Block verwenden! Der GC kann das Objekt verschieben, sobald Sie sich nicht mehr im festen Block befinden.
Dies ist - glaube ich - die einzige Möglichkeit, ein IntPtr für ein Array-Element zu erhalten, das nicht das 0. ist (ohne Verwendung von unsicherem Code).
Möglicherweise möchten Sie untersuchen, ob Sie Ihren AutoPinner von SafeHandle ableiten können, da diese Klasse Parallelitäts- und Sicherheits-Fallstricke berücksichtigt und das empfohlene IDisposable-Muster fördert / verwendet.
Kkahl
0
Marshal.Copy funktioniert, ist aber ziemlich langsam. Schneller ist es, die Bytes in eine for-Schleife zu kopieren. Noch schneller ist es, das Byte-Array in ein Ulong-Array umzuwandeln, so viel Ulong zu kopieren, wie in das Byte-Array passt, und dann die möglichen verbleibenden 7 Bytes zu kopieren (der Pfad, der nicht 8 Bytes ausgerichtet ist). Am schnellsten ist es, das Byte-Array in einer festen Anweisung zu fixieren, wie oben in Tyalis 'Antwort vorgeschlagen.
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;}
Dies ist ein ineffizienteres Duplikat der akzeptierten Antwort. Warum sollten Sie Byte für Byte anstatt auf einmal kopieren?
BDL
Ich habe den Fehler in der akzeptierten Antwort in meinem Code gefunden. Ich habe die C ++ - DLL-Funktion in c # aufgerufen. Der in c ++ verwendete Parameter char *, daher habe ich die akzeptierte Methode verwendet. [DllImport ("MyDll.dll", EntryPoint = "Funktionsname", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)], aber seltsam, wenn die akzeptierte Methode verwendet wird, kann IntPtr nicht korrekt sein wurde ein Puffer als Unicode angezeigt.
Also habe
-6
In einigen Fällen können Sie im Fall von IntPtr einen Int32-Typ (oder Int64) verwenden. Wenn Sie können, ist BitConverter eine weitere nützliche Klasse. Für das, was Sie wollen, können Sie beispielsweise BitConverter.ToInt32 verwenden.
Sie sollten niemals Int32 oder Int64 anstelle eines Zeigers verwenden . Wenn Sie Ihren Code auf eine andere Plattform portieren müssen (32-Bit-> 64-Bit), treten alle Arten von Kopfschmerzen auf.
xxbbcc
5
Nein, es gibt keinen gültigen Fall, in dem Sie a korrekt und sicher Int32als Zeiger verwenden können. Dies war vor Jahren eine schlechte Praxis und führte zu allen möglichen Portierungsproblemen. Auch ein Int64ist nicht sicher, da es bereits 128-Bit-Architekturen gibt und die Zeigergröße zunimmt. Zeiger sollten immer nur als Zeiger dargestellt werden.
xxbbcc
1
Ich habe es in .NET CF-Projekten ohne Probleme verwendet. Natürlich würden Sie Probleme haben, wenn Sie versuchen, es auf andere Systeme zu portieren, aber es gibt Code, der nicht dazu gedacht ist, jemals portiert zu werden.
Alejandro Mezcua
Es ist wahr, dass einige Codes nicht portiert werden sollen, aber die Dinge können sich ziemlich schnell ändern. Selbst wenn Ihre Verwendung auf dem CF gerechtfertigt war (ich weiß nicht), ist es immer noch ein schlechter Rat für eine allgemeine Frage. Das einzig gültige Szenario für die Verwendung von int/ longfür Zeiger ist, wenn die verwendete Sprache kein Konzept für sie hat (z. B. VB6). C # unterstützt Zeiger und hat IntPtr- es ist überhaupt nicht erforderlich, inteinen Zeiger anstelle eines Zeigers zu verwenden. Ich werde meine -1 entfernen, wenn Sie Ihrer Antwort klare Warnungen und eine Erklärung möglicher Probleme hinzufügen.
xxbbcc
3
Nun, Sie sind einen Vergleich der Verwendung von sehr spezifischen Rangier - Code zu dieser allgemeinen Frage über Zeiger in C #. In normalem C # gibt es kein Szenario, in dem dies gültig wäre. Ich weiß, dass es eine alte Frage ist, aber ich habe sie abgelehnt, weil ich darauf gestoßen bin, indem ich nach einer Möglichkeit gesucht habe, Speicher für einen IntPtr zuzuweisen - andere werden dies auch sehen. Ich sehe Ihren Rat als sehr gefährlich an, weil die Leute glauben, sie könnten ein Problem leicht lösen, wenn sie nur zukünftige Probleme haben.
GCHandle
Methode funktioniert wie ein Zauber ... auch diefixed
Methode. : P :))Antworten:
Sie sind sich nicht sicher, ob Sie ein IntPtr in ein Array einbinden möchten, aber Sie können die Daten zur Verwendung mit nicht verwaltetem Code mithilfe von Mashal.Copy kopieren:
Alternativ könnten Sie eine Struktur mit einer Eigenschaft deklarieren und dann Marshal.PtrToStructure verwenden. Dies würde jedoch weiterhin die Zuweisung von nicht verwaltetem Speicher erfordern.
Bearbeiten: Wie Tyalis betonte, können Sie auch festen Code verwenden, wenn unsicherer Code für Sie eine Option ist
quelle
Marshal.Copy
benötigt diese Überlastung einen Startindex. Der Anruf sollte seinMarshal.Copy(bytes, 0, unmanagedPointer, bytes.Length);
Ein anderer Weg,
quelle
Dies sollte funktionieren, muss jedoch in einem unsicheren Kontext verwendet werden:
Vorsicht, Sie müssen den Zeiger im festen Block verwenden! Der GC kann das Objekt verschieben, sobald Sie sich nicht mehr im festen Block befinden.
quelle
Sie können
Marshal.UnsafeAddrOfPinnedArrayElement(array, 0)
einen Speicherzeiger auf das Array abrufen.quelle
Hier ist eine Wendung in der Antwort von @ user65157 (+1 dafür, übrigens):
Ich habe einen IDisposable-Wrapper für das angeheftete Objekt erstellt:
dann benutze es wie folgt:
Ich fand das eine nette Art, nicht zu vergessen, Free () aufzurufen :)
quelle
Marshal.Copy funktioniert, ist aber ziemlich langsam. Schneller ist es, die Bytes in eine for-Schleife zu kopieren. Noch schneller ist es, das Byte-Array in ein Ulong-Array umzuwandeln, so viel Ulong zu kopieren, wie in das Byte-Array passt, und dann die möglichen verbleibenden 7 Bytes zu kopieren (der Pfad, der nicht 8 Bytes ausgerichtet ist). Am schnellsten ist es, das Byte-Array in einer festen Anweisung zu fixieren, wie oben in Tyalis 'Antwort vorgeschlagen.
quelle
quelle
In einigen Fällen können Sie im Fall von IntPtr einen Int32-Typ (oder Int64) verwenden. Wenn Sie können, ist BitConverter eine weitere nützliche Klasse. Für das, was Sie wollen, können Sie beispielsweise BitConverter.ToInt32 verwenden.
quelle
Int32
als Zeiger verwenden können. Dies war vor Jahren eine schlechte Praxis und führte zu allen möglichen Portierungsproblemen. Auch einInt64
ist nicht sicher, da es bereits 128-Bit-Architekturen gibt und die Zeigergröße zunimmt. Zeiger sollten immer nur als Zeiger dargestellt werden.int
/long
für Zeiger ist, wenn die verwendete Sprache kein Konzept für sie hat (z. B. VB6). C # unterstützt Zeiger und hatIntPtr
- es ist überhaupt nicht erforderlich,int
einen Zeiger anstelle eines Zeigers zu verwenden. Ich werde meine -1 entfernen, wenn Sie Ihrer Antwort klare Warnungen und eine Erklärung möglicher Probleme hinzufügen.