2014-10-22T23:55:06の更新内容

programming/netfx/stream/1_3_unmanagedmemorystream/index.wiki.txt

current previous
1,265 0,0
+
${smdncms:title,UnmanagedMemoryStreamクラス}
+
${smdncms:header_title,UnmanagedMemoryStreamクラス (System.IO)}
+
${smdncms:keywords,ポインタ,メモリ,バッファ,固定長,読み込み,書き込み,アンマネージド,UnmanagedMemoryStream}
+
${smdncms:document_versions,codelang=cs,codelang=vb}
+

          
+
#navi(..)
+

          
+
[[MemoryStream>programming/netfx/stream/1_2_memorystream]]がマネージドメモリへの読み書きを行うのに対して、&msdn(netfx,type,System.IO.UnmanagedMemoryStream){UnmanagedMemoryStreamクラス};はポインタで表されるアンマネージメモリに対する読み書きを行うためのStreamです。 ポインタからUnmanagedMemoryStreamクラスのインスタンスを作成する以外の操作方法は[[Streamクラス>programming/netfx/stream/0_abstract]]と同じです。
+

          
+
Streamクラスを使ってポインタに対する読み書きが行えるようになるため、アンマネージメモリに対して[[BinaryReader・BinaryWriter>programming/netfx/stream/3_binaryreader_binarywriter]]を使った読み書きができるようになります。
+

          
+
-関連するページ
+
--[[programming/netfx/tips/convert_struct_and_bytearray]]
+
--[[programming/netfx/disposing]]
+
--[[programming/netfx/arrays/3_structfields]]
+

          
+
#adunit
+

          
+
*UnmanagedMemoryStreamクラス
+
UnmanagedMemoryStreamクラスのインスタンスを作成するには、コンストラクタでアンマネージメモリブロックの先頭を表す``byte*``型のポインタと、メモリブロックの長さを指定します。
+

          
+
#code(cs,UnmanagedMemoryStreamインスタンスの作成){{
+
using System;
+
using System.IO;
+

          
+
class Sample {
+
  public static void Main()
+
  {
+
    byte[] buffer = new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
+

          
+
    unsafe {
+
      // byte配列のポインタを取得する
+
      fixed (byte* ptr = buffer) {
+
        // ポインタとメモリブロックの長さを指定してUnmanagedMemoryStreamを作成する
+
        using (UnmanagedMemoryStream stream = new UnmanagedMemoryStream(ptr, buffer.Length)) {
+
          // 作成したUnmanagedMemoryStreamを使用して1バイトずつ読み込み内容を表示する
+
          for (int i = 0; i < stream.Length; i++) {
+
            Console.WriteLine(stream.ReadByte());
+
          }
+
        }
+
      }
+
    }
+
  }
+
}
+
}}
+

          
+
メモリブロックへの書き込みを行いたい場合は、``FileAccess.Write``または``FileAccess.ReadWrite``を指定します。
+

          
+
#code(cs,書き込みを行うUnmanagedMemoryStreamインスタンスの作成){{
+
using System;
+
using System.IO;
+

          
+
class Sample {
+
  public static void Main()
+
  {
+
    byte[] buffer = new byte[10];
+

          
+
    unsafe {
+
      // byte配列のポインタを取得する
+
      fixed (byte* ptr = buffer) {
+
        // ポインタへの書き込みを行うUnmanagedMemoryStreamを作成する
+
        using (UnmanagedMemoryStream stream = new UnmanagedMemoryStream(ptr, 0, buffer.Length, FileAccess.Write)) {
+
          // 作成したUnmanagedMemoryStreamを使用して1バイトずつ書き込む
+
          for (int i = 0; i < stream.Length; i++) {
+
            stream.WriteByte(0);
+
          }
+
        }
+
      }
+
    }
+
  }
+
}
+
}}
+

          
+
&msdn(netfx,type,System.IntPtr){IntPtr};を使ってUnmanagedMemoryStreamインスタンスを作成することはできないため、VB.NETなどポインタ型を持たない言語では、他の言語で作成されたラッパーを使用するなどするか、&msdn(netfx,type,System.Runtime.InteropServices.Marshal){Marshalクラス};を使用して読み書きを行う必要があります。
+

          
+
UnmanagedMemoryStreamは常に既存のアンマネージメモリブロックを操作します。 [[MemoryStream>programming/netfx/stream/1_2_memorystream]]とは異なり、UnmanagedMemoryStream自体はバッファを確保する機能を持ちません。 また、UnmanagedMemoryStreamはコンストラクタで指定したアンマネージメモリブロックの解放も行いません。 メモリブロックの確保・解放などの管理は使用者側で行う必要があります。
+

          
+
UnmanagedMemoryStreamでは、コンストラクタでアクセス可能なメモリブロックの長さを指定することができます。 あらかじめ指定された範囲を超えてアクセスしようとした場合には&msdn(netfx,type,System.NotSupportedException){NotSupportedException};がスローされます。 また、指定されたFileAccessで許可されていないアクセス(読み込み・書き込み)を行おうとした場合にもNotSupportedExceptionがスローされます。
+

          
+
#code(cs,UnmanagedMemoryStreamとスローされる例外の例){{
+
using System;
+
using System.IO;
+

          
+
class Sample {
+
  public static void Main()
+
  {
+
    byte[] buffer = new byte[8];
+

          
+
    unsafe {
+
      fixed (byte* ptr = buffer) {
+
        // ポインタが表すメモリブロックの先頭から4バイト分までの
+
        // 書き込みのみを許可するUnmanagedMemoryStreamを作成する
+
        using (UnmanagedMemoryStream stream = new UnmanagedMemoryStream(ptr, 4, 4, FileAccess.Write)) {
+
          try {
+
            // FileAccess.Readが指定されていないため、読み込みを行おうとすると
+
            // NotSupportedExceptionがスローされる
+
            stream.ReadByte();
+
          }
+
          catch (NotSupportedException) {
+
          }
+

          
+
          // 4バイト分書き込む
+
          stream.WriteByte(1);
+
          stream.WriteByte(2);
+
          stream.WriteByte(3);
+
          stream.WriteByte(4);
+

          
+
          try {
+
            // コンストラクタで指定した範囲を超えた書き込みとなるため、
+
            // NotSupportedExceptionがスローされる
+
            stream.WriteByte(5);
+
          }
+
          catch (NotSupportedException) {
+
          }
+
        }
+
      }
+
    }
+

          
+
    // 書き込まれた結果を表示する
+
    Console.WriteLine(BitConverter.ToString(buffer));
+
  }
+
}
+
}}
+

          
+
#prompt(実行結果){{
+
01-02-03-04-00-00-00-00
+
}}
+

          
+
インスタンスを作成して以降の操作は、通常のStreamと同様に操作を行うことができます。 また、UnmanagedMemoryStreamと[[BinaryReader・BinaryWriter>programming/netfx/stream/3_binaryreader_binarywriter]]と組み合わせて使用することもできます。
+

          
+
以下の例では、構造体のポインタからUnmanagedMemoryStreamを作成し、BinaryWriterに使って書き込みを行う例です。 この例で使用しているStructLayout属性については[[programming/netfx/structlayout_fieldoffset]]を参照してください。
+

          
+
#code(cs,UnmanagedMemoryStreamとBinaryWriterを使って構造体のポインタへの書き込みを行う例){{
+
using System;
+
using System.IO;
+
using System.Runtime.InteropServices;
+

          
+
class Sample {
+
  [StructLayout(LayoutKind.Sequential, Pack = 1)]
+
  struct ARGB {
+
    public byte A;
+
    public byte R;
+
    public byte G;
+
    public byte B;
+
  }
+

          
+
  public static void Main()
+
  {
+
    ARGB color = new ARGB();
+

          
+
    unsafe {
+
      // 構造体のポインタを取得
+
      ARGB* ptr = &color;
+

          
+
      // 取得した構造体のポインタへの書き込みを行うUnmanagedMemoryStreamを作成
+
      using (UnmanagedMemoryStream stream = new UnmanagedMemoryStream((byte*)ptr, 0, sizeof(ARGB), FileAccess.Write)) {
+
        BinaryWriter writer = new BinaryWriter(stream);
+

          
+
        // 4バイト分書き込む
+
        writer.Write((uint)0xAABBCCFF);
+
      }
+
    }
+

          
+
    // 書き込まれた内容を表示する
+
    Console.WriteLine("A=0x{0:X2} R=0x{1:X2} G=0x{2:X2} B=0x{3:X2}", color.A, color.R, color.G, color.B);
+
  }
+
}
+
}}
+

          
+
#prompt(実行結果){{
+
A=0xFF R=0xCC G=0xBB B=0xAA
+
}}
+

          
+

          
+
*UnmanagedMemoryStreamクラスの使用例
+

          
+
**バイト配列に変換
+
以下はUnmanagedMemoryStreamクラスを使って、メモリブロックの内容をバイト配列に変換する例です。
+

          
+
#code(cs){{
+
using System;
+
using System.IO;
+
using System.Runtime.InteropServices;
+

          
+
class Sample {
+
  /// <summary>ポインタの内容をバイト配列に変換するメソッド</summary>
+
  static unsafe byte[] PtrToByteArray(void* ptr, long length)
+
  {
+
    var buffer = new byte[length];
+

          
+
    using (UnmanagedMemoryStream stream = new UnmanagedMemoryStream((byte*)ptr, length, length, FileAccess.Read)) {
+
      // ポインタより作成したUnmanagedMemoryStreamから全データをバイト配列に読み込む
+
      stream.Read(buffer, 0, (int)stream.Length);
+
    }
+

          
+
    return buffer;
+
  }
+

          
+
  public static void Main()
+
  {
+
    IntPtr ptr = IntPtr.Zero;
+

          
+
    try {
+
      ptr = Marshal.AllocHGlobal(0x10);
+

          
+
      unsafe {
+
        byte[] arr = PtrToByteArray(ptr.ToPointer(), 0x10);
+

          
+
        Console.WriteLine(BitConverter.ToString(arr));
+
      }
+
    }
+
    finally {
+
      if (ptr != IntPtr.Zero)
+
        Marshal.FreeHGlobal(ptr);
+
    }
+
  }
+
}
+
}}
+

          
+
**memcpy/CopyMemory
+
以下はUnmanagedMemoryStreamクラスでmemcpy/CopyMemory相当の処理を行う例です。 コピー元・コピー先ブロックのポインタそれぞれからUnmanagedMemoryStreamインスタンスを作成し、&msdn(netfx,member,System.IO.UnmanagedMemoryStream.CopyTo){CopyToメソッド};でコピーを行います。
+

          
+
#code(cs){{
+
using System;
+
using System.IO;
+
using System.Runtime.InteropServices;
+

          
+
class Sample {
+
  /// <summary>ポインタの内容を指定されたポインタにコピーするメソッド</summary>
+
  static unsafe void CopyMemory(void* destination, void* source, long length)
+
  {
+
    using (UnmanagedMemoryStream streamSource = new UnmanagedMemoryStream((byte*)source, length, length, FileAccess.Read)) {
+
      using (UnmanagedMemoryStream streamDestination = new UnmanagedMemoryStream((byte*)destination, 0, length, FileAccess.Write)) {
+
        streamSource.CopyTo(streamDestination);
+
      }
+
    }
+
  }
+

          
+
  public static void Main()
+
  {
+
    IntPtr ptrSource = IntPtr.Zero;
+
    IntPtr ptrDestination = IntPtr.Zero;
+

          
+
    try {
+
      ptrSource = Marshal.AllocHGlobal(0x10);
+
      ptrDestination = Marshal.AllocHGlobal(0x10);
+

          
+
      unsafe {
+
        CopyMemory(ptrDestination.ToPointer(), ptrSource.ToPointer(), 0x10);
+
      }
+
    }
+
    finally {
+
      if (ptrSource != IntPtr.Zero)
+
        Marshal.FreeHGlobal(ptrSource);
+
      if (ptrDestination != IntPtr.Zero)
+
        Marshal.FreeHGlobal(ptrDestination);
+
    }
+
  }
+
}
+
}}
+

          
+
なお、CopyToメソッドは.NET Framework 4以降で使用可能なメソッドであるため、.NET Framework 3.5以前の場合はReadメソッド・Writeメソッドを使ってコピー処理を記述する必要があります。 (関連:[[programming/netfx/stream/0_abstract#Stream.CopyTo]])
+

          
+
#navi(..)
+