Saya ingin meneruskan byte[]ke metode mengambil IntPtrParameter dalam C #, apakah itu mungkin dan bagaimana?
GCHandlemetode berfungsi seperti pesona ... juga fixedmetodenya. : P :))
Saya ingin meneruskan byte[]ke metode mengambil IntPtrParameter dalam C #, apakah itu mungkin dan bagaimana?
GCHandlemetode berfungsi seperti pesona ... juga fixedmetodenya. : P :))
Jawaban:
Tidak yakin tentang mendapatkan IntPtr ke array, tetapi Anda dapat menyalin data untuk digunakan dengan kode yang tidak dikelola dengan menggunakan Mashal.Copy:
IntPtr unmanagedPointer = Marshal.AllocHGlobal(bytes.Length);
Marshal.Copy(bytes, 0, unmanagedPointer, bytes.Length);
// Call unmanaged code
Marshal.FreeHGlobal(unmanagedPointer);
Atau Anda bisa mendeklarasikan struct dengan satu properti dan kemudian menggunakan Marshal.PtrToStructure, tetapi itu masih membutuhkan alokasi memori yang tidak dikelola.
Sunting: Selain itu, seperti yang ditunjukkan Tyalis, Anda juga dapat menggunakan fixed jika kode yang tidak aman adalah pilihan untuk Anda
Marshal.Copydengan kelebihan itu perlu indeks awal. Panggilan harusMarshal.Copy(bytes, 0, unmanagedPointer, bytes.Length);
Cara lain,
GCHandle pinnedArray = GCHandle.Alloc(byteArray, GCHandleType.Pinned);
IntPtr pointer = pinnedArray.AddrOfPinnedObject();
// Do your stuff...
pinnedArray.Free();
Ini harus berfungsi tetapi harus digunakan dalam konteks yang tidak aman:
byte[] buffer = new byte[255];
fixed (byte* p = buffer)
{
IntPtr ptr = (IntPtr)p;
// do you stuff here
}
Waspadalah, Anda harus menggunakan pointer di blok tetap! Gc dapat memindahkan objek setelah Anda tidak lagi berada di blok tetap.
Anda bisa menggunakan Marshal.UnsafeAddrOfPinnedArrayElement(array, 0)untuk mendapatkan pointer memori ke array.
Berikut ini twist pada jawaban @ user65157 (+1 untuk itu, BTW):
Saya membuat pembungkus IDisposable untuk objek yang disematkan:
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();
}
}
kemudian gunakan seperti ini:
using (AutoPinner ap = new AutoPinner(MyManagedObject))
{
UnmanagedIntPtr = ap; // Use the operator to retrieve the IntPtr
//do your stuff
}
Saya menemukan ini sebagai cara yang bagus untuk tidak lupa menelepon Gratis () :)
Marshal. Salinan bekerja tetapi agak lambat. Lebih cepat adalah menyalin byte dalam for loop. Bahkan lebih cepat adalah dengan melemparkan array byte ke array ulong, salin sebanyak ulong sesuai dengan array byte, kemudian salin sisa 7 byte yang tersisa (jejak yang tidak selaras 8 byte). Tercepat adalah pin array byte dalam pernyataan tetap seperti yang diusulkan di atas dalam jawaban 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;
}
Dalam beberapa kasus, Anda dapat menggunakan tipe Int32 (atau Int64) untuk IntPtr. Jika Anda bisa, kelas berguna lainnya adalah BitConverter. Untuk apa yang Anda inginkan, Anda bisa menggunakan BitConverter.ToInt32 misalnya.
Int32sebagai penunjuk. Ini adalah praktik buruk yang dilakukan bertahun-tahun yang lalu dan itu menyebabkan semua jenis masalah porting. Bahkan Int64tidak aman karena sudah ada arsitektur 128-bit dan ukuran pointer akan meningkat. Pointer seharusnya hanya direpresentasikan sebagai pointer.
int/ longuntuk pointer adalah ketika bahasa yang digunakan tidak memiliki konsep mereka (misalnya VB6). C # mendukung pointer dan memiliki IntPtr- tidak perlu sama sekali untuk menggunakan intdi tempat pointer. Saya akan menghapus -1 saya jika Anda menambahkan peringatan yang jelas dan penjelasan tentang kemungkinan masalah pada jawaban Anda.