So erhalten Sie IntPtr aus Byte [] in C #

127

Ich möchte ein byte[]an eine Methode übergeben, nimmt einen IntPtrParameter in C #, ist das möglich und wie?

Netseng
quelle
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:

IntPtr unmanagedPointer = Marshal.AllocHGlobal(bytes.Length);
Marshal.Copy(bytes, 0, unmanagedPointer, bytes.Length);
// Call unmanaged code
Marshal.FreeHGlobal(unmanagedPointer);

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

Richard Szalay
quelle
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();
user65157
quelle
1
@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 = new byte[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.

Michaël Carpentier
quelle
16
Ich mag diese Antwort, weil es nicht darum geht, zusätzlichen Speicher
zuzuweisen,
3
Dies ist die beste Antwort, wenn ein großes Byte [] verwendet wird. Eine Kopie hat zu viel Overhead
goodguys_activate
19

Sie können Marshal.UnsafeAddrOfPinnedArrayElement(array, 0)einen Speicherzeiger auf das Array abrufen.

Sakthi
quelle
1
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).
Guido Domenici
5
Um diesen Code zuverlässig zu machen, sollte das Array zuerst an msdn.microsoft.com/en-us/library/3k4y07x3.aspx
sergtk
13

Hier ist eine Wendung in der Antwort von @ user65157 (+1 dafür, übrigens):

Ich habe einen IDisposable-Wrapper für das angeheftete Objekt erstellt:

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();
   }
}

dann benutze es wie folgt:

using (AutoPinner ap = new AutoPinner(MyManagedObject))
{
   UnmanagedIntPtr = ap;  // Use the operator to retrieve the IntPtr
   //do your stuff
}

Ich fand das eine nette Art, nicht zu vergessen, Free () aufzurufen :)

Dlchambers
quelle
4
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.

David Burg
quelle
-1
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;
}
nexdev
quelle
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.

Alejandro Mezcua
quelle
14
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.
xxbbcc