7

C#.NETバージョン2でバイトの配列を構造に直接取得する方法を教えてもらえますか?Cで見られるようなおなじみのfreadように、これまでのところ、バイトのストリームを読み取って構造体を自動的に埋めることはあまり成功していません。キーワードを使用してマネージコードにポインターhocus-pocusがある実装を見てきましたunsafe

このサンプルを見てください:

public unsafe struct foobarStruct{

   /* fields here... */

   public foobarStruct(int nFakeArgs){
      /* Initialize the fields... */
   }

   public foobarStruct(byte[] data) : this(0) {
      unsafe {
         GCHandle hByteData = GCHandle.Alloc(data, GCHandleType.Pinned);
         IntPtr pByteData = hByteData.AddrOfPinnedObject();
         this = (foobarStruct)Marshal.PtrToStructure(pByteData, this.GetType());
         hByteData.Free();
      }
   }
}

私が2つのコンストラクターを持っている理由foobarStruct

  • 空のコンストラクターは存在できませんか?
  • 構造体をインスタンス化するときに、メモリのブロックを(バイト配列として)コンストラクターに渡します。

その実装は十分に良いですか、それともこれを達成するためのはるかにクリーンな方法がありますか?

編集: ISerializableインターフェースまたはその実装を使用したくありません。使用されているフィールドを計算し、PE構造を使用してそのデータを決定するために、バイナリイメージを読み取ろうとしています。

4

2 に答える 2

10

P/Invoke マーシャラーを使用することに問題はありません。安全ではないため、unsafe キーワードを使用する必要はありません。それを間違えると、悪いデータが生成されるだけです。特にファイルに文字列が含まれている場合は、逆シリアル化コードを明示的に記述するよりもはるかに簡単に使用できます。BinaryReader.ReadString() は使用できません。文字列が BinaryWriter によって書き込まれたものと見なされます。ただし、構造体宣言でデータの構造を宣言していることを確認してください。 this.GetType() はうまく機能しない可能性があります。

以下は、任意の構造体宣言で機能するジェネリック クラスです。

  class StructureReader<T> where T : struct {
    private byte[] mBuffer;
    public StructureReader() {
      mBuffer = new byte[Marshal.SizeOf(typeof(T))];
    }
    public T Read(System.IO.FileStream fs) {
      int bytes = fs.Read(mBuffer, 0, mBuffer.Length);
      if (bytes == 0) throw new InvalidOperationException("End-of-file reached");
      if (bytes != mBuffer.Length) throw new ArgumentException("File contains bad data");
      T retval;
      GCHandle hdl = GCHandle.Alloc(mBuffer, GCHandleType.Pinned);
      try {
        retval = (T)Marshal.PtrToStructure(hdl.AddrOfPinnedObject(), typeof(T));
      }
      finally {
        hdl.Free();
      }
      return retval;
    }

ファイル内のデータ構造のサンプル宣言:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
struct Sample {
  [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 42)]
  public string someString;
}

ファイル内のデータと一致するように、構造宣言と属性を微調整する必要があります。ファイルを読み取るサンプル コード:

  var data = new List<Sample>();
  var reader = new StructureReader<Sample>();
  using (var stream = new FileStream(@"c:\temp\test.bin", FileMode.Open, FileAccess.Read)) {
    while(stream.Position < stream.Length) {
      data.Add(reader.Read(stream));
    }
  }
于 2009-12-20T16:04:01.270 に答える
3

おそらくBinaryReader、バイナリ形式でプリミティブ型を読み取ることができるaを使用することをお勧めします。

MemoryStreamからを作成しbyte[]、それからを使用BinaryReaderします。構造を読み取り、それに応じてオブジェクトを入力できるはずです。

于 2009-12-20T14:09:38.480 に答える