C # 'da [] bayttan IntPtr nasıl alınır


127

byte[]Bir yönteme geçmek istiyorum IntPtrC # bir Parametre alır , bu mümkün mü ve nasıl?


Daha fazla ayrıntı verebilir misiniz? Bunu neden yapmak istersiniz?
Grzenio

2
Örneğin DirectShow API kullanıyorsanız buna ihtiyacınız var ... VideoRenderer'dan veri almak için bunu kullanmanız gerekir ... ve GCHandleyöntem bir cazibe gibi çalışır ... aynı zamanda fixedyöntem. : P :))
Cipi

Terabaytlarca veri aktaran her şey için buna ihtiyacınız var ve fazladan kopyadan kaçınmak istiyorsunuz. Hayal gücünü kullan.
Brain2000

Yanıtlar:


93

Bir diziye bir IntPtr alma konusunda emin değilsiniz, ancak Mashal.Copy kullanarak verileri yönetilmeyen kodla kullanmak için kopyalayabilirsiniz:

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

Alternatif olarak, bir özellik içeren bir yapı bildirebilir ve ardından Marshal.PtrToStructure kullanabilirsiniz, ancak bu yine de yönetilmeyen bellek ayırmayı gerektirir.

Düzenleme: Ayrıca, Tyalis'in belirttiği gibi, güvenli olmayan kod sizin için bir seçenekse, düzeltildi de kullanabilirsiniz.


Sadece açıklığa kavuşturmak için, Marshal.Copybu aşırı yük ile bir başlangıç ​​indeksine ihtiyaç var. ÇağrıMarshal.Copy(bytes, 0, unmanagedPointer, bytes.Length);
mkenyon

@ user65157'nin cevabı gibi, yeni bellek oluşturmadan IntPtr almak daha iyidir.
Lin

208

Diğer yol,

GCHandle pinnedArray = GCHandle.Alloc(byteArray, GCHandleType.Pinned);
IntPtr pointer = pinnedArray.AddrOfPinnedObject();
// Do your stuff...
pinnedArray.Free();

1
@Cipi Eric Lipperts'in diğer gönderilerinden birine göre, bu, GC kullanmak yerine Sabit anahtar kelimesine sahip olmalıdır
goodguys_activate

çok teşekkürler. Güzel çalışın ve oldukça kolay. GCHandles ve Marshal konusunda o kadar yetenekli değilim. Birisi bana Mareşal'in hangi artılarının olduğunu ve GCHandle'ın ne olduğunu ve hangisinin nerede kullanıldığını söyleyebilir mi? Teşekkürler
piggy

1
Birisi lütfen Eric Lippert gönderisine referans verebilir mi?
Cameron

3
@piggy: Bence Marshal'ın dezavantajı, verilerinizin bir kopyasını almanız gerektiğidir (bu, uzun zaman alabilir ve ihtiyacınız olabilecek hafızayı boşa harcayabilir)
Riki

6
@ makerofthings7 Lippert'in GC yerine "sabit" kullanılacağını söylediğine inanmıyorum [nesneleri sabitlemek için], bir nesneyi süresiz olarak sabitlemek için GC'yi kullanmayın, bunu yapmak için sabit kullanmayın dediğine inanıyorum. aynı.
Cameron

129

Bu işe yaramalı ancak güvenli olmayan bir bağlamda kullanılmalıdır:

byte[] buffer = new byte[255];
fixed (byte* p = buffer)
{
    IntPtr ptr = (IntPtr)p;
    // do you stuff here
}

dikkat edin, sabit bloktaki işaretçiyi kullanmanız gerekir! Artık sabit blokta olmadığınızda gc nesneyi hareket ettirebilir.


16
Bu yanıtı beğendim çünkü sadece verilere erişmek için fazladan bellek ayırmayı gerektirmiyor
Xcalibur

3
Büyük bayt [] kullanılıyorsa bu en iyi yanıttır. Bir kopyanın çok fazla ek yükü var
goodguys_activate


13

İşte @ user65157'nin cevabında bir değişiklik (bunun için +1, BTW):

Sabitlenmiş nesne için IDisposable bir sarmalayıcı oluşturdum:

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

sonra bunu şu şekilde kullanın:

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

Bunu Free () aramayı unutmamanın güzel bir yolu olarak buldum :)


4
AutoPinner'ınızı SafeHandle'dan türetmeyi araştırmak isteyebilirsiniz, çünkü bu sınıf eşzamanlılık ve güvenlik "getchas" ları hesaba katar ve ayrıca önerilen IDisposable kalıbı teşvik eder / kullanır.
kkahl

0

Marshal.Copy işe yarıyor ama oldukça yavaş. Daha hızlı, baytları bir for döngüsünde kopyalamaktır. Daha da hızlı olanı, bayt dizisini çok uzun bir diziye dönüştürmek, bayt dizisine sığabildiği kadar çok sayıda kopyalamak ve ardından kalan olası 7 baytı (8 bayt hizalı olmayan iz) kopyalamaktır. En hızlısı, yukarıda Tyalis'in cevabında önerildiği gibi bayt dizisini sabit bir ifadeye sabitlemektir.


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

Bu, kabul edilen cevabın daha verimsiz bir kopyasıdır. Neden hepsini aynı anda yerine bayt bayt kopyalayasınız?
BDL

Kodumdaki kabul edilen cevapta hatayı buldum. C # 'da C ++ dll işlevini çağırdım. char * parametresi c ++ 'da kullanıldı, bu yüzden kabul edilen yöntemi kullandım. [DllImport ("MyDll.dll", EntryPoint = "functionName", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] ama garip, kabul edilen yöntemi kullanırken doğru IntPtr alamıyorum, arabelleğin bir kısmı bozuk bazı tamponlar Unicode olarak görüntülendi. bu yüzden bu yöntemi kullandım
nexdev

-6

Bazı durumlarda, IntPtr durumunda bir Int32 türü (veya Int64) kullanabilirsiniz. Yapabiliyorsanız, başka bir kullanışlı sınıf BitConverter'dır. İstediğiniz şey için örneğin BitConverter.ToInt32'yi kullanabilirsiniz.


14
Bir işaretçi yerine asla ve asla Int32 veya Int64 kullanmamalısınız . Kodunuzu farklı bir platforma (32-bit-> 64-bit) taşımanız gerekiyorsa, her türlü baş ağrınız olur.
xxbbcc

5
Hayır, Int32işaretçi olarak doğru ve güvenli bir şekilde kullanabileceğiniz geçerli bir durum yoktur . Bu yıllar önce yapılan kötü bir uygulamaydı ve her türlü taşıma sorununa yol açtı. Bir bile Int64güvenli değildir çünkü zaten 128 bit mimariler vardır ve işaretçi boyutu artacaktır. İşaretçiler yalnızca işaretçiler olarak temsil edilmelidir.
xxbbcc

1
Bunu .NET CF projelerinde sorunsuz kullandım, elbette başka sistemlere taşımaya çalışırsanız sorun yaşarsınız, ancak dışarıda hiçbir zaman taşınması gerekmeyen kodlar var.
Alejandro Mezcua

Bazı kodların taşınmasının planlanmadığı doğrudur, ancak işler oldukça hızlı değişebilir. CF'deki kullanımınız haklı olsa bile (bilmiyorum), genel bir soru için hala kötü bir tavsiye. İşaretçiler için int/ için kullanmanın tek geçerli senaryosu, kullanılan longdilin bunlarla ilgili bir kavramının olmadığı durumdur (örneğin, VB6). C # işaretçileri destekler ve sahiptir IntPtr- intbir işaretçi yerine bir işaretçi kullanmaya hiç gerek yoktur . Cevabınıza net uyarılar ve olası sorunların açıklamasını eklerseniz -1'imi kaldırırım.
xxbbcc

3
Pekala, çok özel bir sıralama kodunun kullanımını, C # 'da işaretçilerin kullanımıyla ilgili bu genel soru ile karşılaştırıyorsunuz. Normal C # 'da bunun geçerli olacağı bir senaryo yoktur. Bunun eski bir soru olduğunu biliyorum ama oy verdim çünkü bir IntPtr için bellek ayırmanın bir yolunu arayarak karşılaştım - diğerleri de bunu görecek. Tavsiyenizi çok tehlikeli olarak görüyorum çünkü insanlar, sahip oldukları tek şey gelecekte bir sorun olduğunda, bir sorunu kolayca çözdüklerini düşünerek onunla devam edecekler.
xxbbcc
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.