C#でbyte []からIntPtrを取得する方法


127

C#でパラメーターをbyte[]取るメソッドにa を渡したいのですIntPtrが、それは可能ですか?


詳細を教えてください。なぜそれをしたいのですか?
Grzenio 2009

2
たとえば、DirectShow APIを使用する場合にこれが必要です... VideoRendererからデータを取得するには、これを使用する必要があります...そしてGCHandleメソッドはチャームのように機能します... fixedメソッドでもあります。:P :))
Cipi

テラバイト単位のデータを転送するものにはこれが必要であり、余分なコピーを避けたいと考えています。想像力を使って。
Brain2000

回答:


93

IntPtrを配列に取得するかどうかはわかりませんが、Mashal.Copyを使用して、アンマネージコードで使用するためにデータをコピーできます。

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

または、1つのプロパティで構造体を宣言してから、Marshal.PtrToStructureを使用することもできますが、その場合でも、アンマネージメモリを割り当てる必要があります。

編集:また、Tyalisが指摘したように、安全でないコードがオプションである場合は、fixedを使用することもできます


明確にするためにMarshal.Copy、そのオーバーロードには開始インデックスが必要です。コールはする必要がありますMarshal.Copy(bytes, 0, unmanagedPointer, bytes.Length);
mkenyon

@ user65157の回答のように、新しいメモリを作成せずにIntPtrを取得することをお勧めします。
Lin

208

別の方法、

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

1
@Cipi Eric Lippertsの他の投稿によると、GCを使用する代わりに、Fixedキーワードが必要です
goodguys_activate

どうもありがとう。うまく機能し、それはかなり簡単です。私はGCHandlesとマーシャルにはそれほど熟練していません。誰かがマーシャルを持っているプラ​​ス、GCHandle、そしてどこでどのハンドルを使用しているのか誰かに教えてもらえますか?ありがとう
piggy

1
誰かがEric Lippertの投稿への参照を提供してくれませんか?
キャメロン

3
@piggy:Marshalの欠点は、データのコピーを作成する必要があることだと思います(これには長い時間がかかり、必要なメモリを浪費する可能性があります)
Riki

6
@ makerofthings7 LippertがGCの代わりに「固定」を使用するように言っているとは思わない[オブジェクトを固定する]、彼はGCを使用してオブジェクトを無期限に固定しないでください。同じです。
Cameron、

129

これは機能するはずですが、安全でないコンテキスト内で使用する必要があります。

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

注意してください、あなたは固定ブロックでポインタを使わなければなりません!固定ブロックから離れると、GCはオブジェクトを移動できます。


16
データにアクセスするためだけに追加のメモリを割り当てる必要がないため、この回答が好きです
Xcalibur

3
大きなbyte []を使用する場合、これが最良の答えです。コピーのオーバーヘッドが大きすぎる
goodguys_activate

19

Marshal.UnsafeAddrOfPinnedArrayElement(array, 0)配列へのメモリポインタを取得するために使用できます。


1
これは-私が思う-(安全でないコードを使用せずに)0番目でない配列要素のIntPtrを取得する唯一の方法です。
グイドドメニチ

5
このコードを信頼できるものにするには、まず配列を固定する必要がありますmsdn.microsoft.com/en-us/library/3k4y07x3.aspx
sergtk 2013年

13

@ user65157の答えにひねりを加えました(そのための+ 1、BTW)。

固定されたオブジェクトのIDisposableラッパーを作成しました。

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

次に、このように使用します:

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

これは、Free()を呼び出すのを忘れないための良い方法であることがわかりました:)


4
AutoPinnerのSafeHandleからの派生を調査することをお勧めします。そのクラスは、同時実行性とセキュリティの「問題」を考慮し、推奨されるIDisposableパターンを推奨/使用するためです。
kkahl 16

0

Marshal.Copyは機能しますが、かなり低速です。より高速なのは、forループでバイトをコピーすることです。さらに高速なのは、バイト配列をulong配列にキャストし、バイト配列に収まるだけのulongをコピーしてから、残りの可能な7バイト(8バイトに整列していない証跡)をコピーすることです。最速は、上記のTyalisの回答で提案されているように、固定されたステートメントでバイト配列を固定することです。


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

これは、受け入れられた回答のより非効率的な複製です。なぜ一度に全部ではなくバイト単位でコピーするのですか?
BDL

私のコードで受け入れられた回答にエラーが見つかりました。C#でC ++ dll関数を呼び出しました。char *パラメータはc ++で使用されるため、受け入れられた方法を使用しました。[DllImport( "MyDll.dll"、EntryPoint = "functionName"、CharSet = CharSet.Ansi、CallingConvention = CallingConvention.Cdecl)]奇妙ですが、受け入れられたメソッドを使用すると、正しいIntPtrを取得できず、一部のバッファが壊れました、一部のバッファがUnicodeとして表示されていました。それで私はこの方法を使いました
nexdev

-6

IntPtrの場合は、Int32型(またはInt64)を使用できる場合があります。可能であれば、もう1つの便利なクラスはBitConverterです。たとえば、BitConverter.ToInt32を使用できます。


14
ポインタの代わりにInt32またはInt64を使用しないでください。コードを別のプラットフォーム(32ビット-> 64ビット)に移植する必要がある場合、あらゆる種類の問題が発生します。
xxbbcc

5
いいえ、をInt32ポインタとして正しく安全に使用できる有効なケースはありません。これは何年も前に行われた悪い習慣であり、あらゆる種類の移植の問題につながりました。Int64128ビットアーキテクチャが既に存在し、ポインタサイズが大きくなるため、安全ではありません。ポインターは、ポインターとしてのみ表現する必要があります。
xxbbcc

1
私は.NET CFプロジェクトで問題なく使用しました。もちろん、他のシステムに移植しようとすると問題が発生しますが、移植されることを意図していないコードがあります。
Alejandro Mezcua

一部のコードは移植される予定がないことは事実ですが、物事はすぐに変わる可能性があります。CFでの使用が正当化されたとしても(私にはわかりません)、それでも一般的な質問に対する悪いアドバイスです。ポインターにint/ を使用するための唯一の有効なシナリオlongは、使用される言語にそれらの概念がない場合です(たとえば、VB6)。C#はポインターをサポートしており、ポインターの代わりにIntPtrを使用する必要はまったくありませんint。明確な警告と潜在的な問題の説明を回答に追加した場合、私は-1を削除します。
xxbbcc

3
さて、あなたはしている C#でポインタを使用してについては、この一般的な質問には非常に特定のマーシャリングコードの使用状況を比較します。通常のC#では、これが有効になるシナリオはありません。私はそれが古い質問であることを知っていますが、IntPtrにメモリを割り当てる方法を探してそれを見つけたので、反対票を投じました-他の人もこれを見るでしょう。あなたのアドバイスは非常に危険だと思います。なぜなら、人々は将来のトラブルだけで問題を簡単に解決できたと思って、それを受け入れてくれるからです。
xxbbcc
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.