Ich möchte ein byte[]an eine Methode übergeben, nimmt einen IntPtrParameter in C #, ist das möglich und wie?
GCHandleMethode funktioniert wie ein Zauber ... auch die fixedMethode. : P :))
Ich möchte ein byte[]an eine Methode übergeben, nimmt einen IntPtrParameter in C #, ist das möglich und wie?
GCHandleMethode funktioniert wie ein Zauber ... auch die fixedMethode. : 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:
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
Marshal.Copybenötigt diese Überlastung einen Startindex. Der Anruf sollte seinMarshal.Copy(bytes, 0, unmanagedPointer, bytes.Length);
Ein anderer Weg,
GCHandle pinnedArray = GCHandle.Alloc(byteArray, GCHandleType.Pinned);
IntPtr pointer = pinnedArray.AddrOfPinnedObject();
// Do your stuff...
pinnedArray.Free();
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.
Sie können Marshal.UnsafeAddrOfPinnedArrayElement(array, 0)einen Speicherzeiger auf das Array abrufen.
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 :)
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.
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 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.
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.
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.