Cara mendapatkan IntPtr dari byte [] di C #

127

Saya ingin meneruskan byte[]ke metode mengambil IntPtrParameter dalam C #, apakah itu mungkin dan bagaimana?

netseng
sumber
Bisakah Anda memberikan detail lebih lanjut? Mengapa Anda ingin melakukannya?
Grzenio
2
Anda memerlukan ini jika Anda menggunakan DirectShow API misalnya ... untuk mendapatkan data dari VideoRenderer, Anda harus menggunakan ini ... dan GCHandlemetode berfungsi seperti pesona ... juga fixedmetodenya. : P :))
Cipi
Anda memerlukan ini untuk apa pun yang mentransfer data terrabyte dan Anda ingin menghindari salinan tambahan. Gunakan imajinasimu.
Brain2000

Jawaban:

93

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

Richard Szalay
sumber
Hanya untuk memperjelas, Marshal.Copydengan kelebihan itu perlu indeks awal. Panggilan harusMarshal.Copy(bytes, 0, unmanagedPointer, bytes.Length);
mkenyon
lebih baik untuk mendapatkan IntPtr tanpa membuat memori baru, seperti jawaban @ user65157.
Lin
208

Cara lain,

GCHandle pinnedArray = GCHandle.Alloc(byteArray, GCHandleType.Pinned);
IntPtr pointer = pinnedArray.AddrOfPinnedObject();
// Do your stuff...
pinnedArray.Free();
pengguna65157
sumber
1
@Cipi Per salah satu posting Eric Lipperts lainnya, ini harus memiliki kata kunci Tetap alih-alih menggunakan GC
goodguys_activate
Terima kasih banyak. Bekerja dengan baik dan sangat mudah. Saya tidak begitu ahli dalam GCHandles dan Marshal. Dapatkah seseorang mengatakan kepada saya apa plus yang dimiliki Marshal dan apa GCHandle dan di mana menggunakan yang mana? Terima kasih
piggy
1
Bisakah seseorang memberikan referensi ke pos Eric Lippert?
Cameron
3
@piggy: Saya pikir kerugian Marshal adalah Anda harus membuat salinan data Anda (yang bisa memakan waktu lama dan membuang-buang memori yang mungkin Anda butuhkan)
Riki
6
@ makerofthings7 Saya tidak percaya Lippert mengatakan untuk menggunakan 'tetap' bukan GC [untuk menyematkan objek], saya percaya dia mengatakan jangan menggunakan GC untuk menyematkan objek tanpa batas, bersama dengan mengatakan jangan gunakan tetap untuk melakukan sama juga.
Cameron
129

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.

Michaël Carpentier
sumber
16
Saya suka jawaban ini karena tidak melibatkan mengalokasikan memori tambahan hanya untuk mengakses data
Xcalibur
3
Ini adalah jawaban terbaik jika byte besar [] digunakan. Salinan memiliki terlalu banyak overhead
goodguys_activate
19

Anda bisa menggunakan Marshal.UnsafeAddrOfPinnedArrayElement(array, 0)untuk mendapatkan pointer memori ke array.

Sakthi
sumber
1
Ini - saya pikir - satu-satunya cara untuk mendapatkan IntPtr untuk elemen array yang bukan 0 (tanpa menggunakan kode yang tidak aman).
Guido Domenici
5
Untuk membuat kode array yang handal ini harus disematkan pertama msdn.microsoft.com/en-us/library/3k4y07x3.aspx
sergtk
13

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 () :)

dlchambers
sumber
4
Anda mungkin ingin menyelidiki untuk mendapatkan AutoPinner Anda dari SafeHandle karena kelas tersebut memperhitungkan "gotchas" keamanan dan penghitungan, serta mendorong / menggunakan pola IDisposable yang direkomendasikan.
kkahl
0

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.

David Burg
sumber
-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
sumber
Ini adalah duplikat yang lebih tidak efisien dari jawaban yang diterima. Mengapa Anda menyalin byte demi byte alih-alih sekaligus?
BDL
Saya menemukan kesalahan dalam jawaban yang diterima dalam kode saya. Saya memanggil fungsi C ++ dll di c #. Parameter char * digunakan dalam c ++, jadi saya menggunakan metode yang diterima. [DllImport ("MyDll.dll", EntryPoint = "functionName", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] tetapi anehnya, ketika menggunakan metode yang diterima, tidak bisa mendapatkan IntPtr yang benar, saya mendapat beberapa buffer rusak , beberapa buffer ditampilkan sebagai Unicode. jadi saya menggunakan metode ini
nexdev
-6

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.

Alejandro Mezcua
sumber
14
Anda seharusnya tidak pernah menggunakan Int32 atau Int64 sebagai pengganti pointer . Jika Anda harus mem-porting kode Anda ke platform lain (32-bit-> 64-bit), Anda akan mengalami semua jenis sakit kepala.
xxbbcc
5
Tidak, tidak ada kasus yang valid di mana Anda dapat menggunakan dengan benar dan aman 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.
xxbbcc
1
Saya menggunakannya dalam proyek .NET CF tanpa masalah, tentu saja Anda akan memiliki masalah jika Anda mencoba untuk port itu ke sistem lain, tetapi ada kode di luar sana yang tidak dimaksudkan untuk diangkut.
Alejandro Mezcua
Memang benar bahwa beberapa kode tidak direncanakan untuk porting tetapi hal-hal dapat berubah dengan cepat. Bahkan jika penggunaan Anda pada CF dibenarkan (saya tidak tahu) itu masih saran buruk untuk pertanyaan umum. Satu-satunya skenario yang valid untuk menggunakan 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.
xxbbcc
3
Nah, Anda yang membandingkan penggunaan kode marshalling yang sangat spesifik untuk pertanyaan umum ini tentang menggunakan pointer di C #. Dalam C # normal tidak ada skenario di mana ini akan valid. Saya tahu ini adalah pertanyaan lama tetapi saya menolaknya karena saya menemukannya dengan mencari cara untuk mengalokasikan memori untuk IntPtr - orang lain akan melihatnya juga. Saya melihat saran Anda sangat berbahaya karena orang-orang akan pergi dengan berpikir mereka lolos dengan menyelesaikan masalah dengan mudah ketika semua yang mereka dapatkan adalah masalah di masa depan.
xxbbcc