7

谁能告诉我如何在 C# .NET 版本 2 中以直接方式将字节数组放入结构中?就像 C 中熟悉fread的一样,到目前为止,我在读取字节流和自动填充结构方面没有取得太大成功。unsafe我已经看到一些实现,其中使用关键字在托管代码中存在指针 hocus-pocus 。

看看这个样本:

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

我有两个构造函数的原因foobarStruct

  • 是否不能有一个空的构造函数。
  • 实例化结构时,将一块内存(作为字节数组)传入构造函数。

该实现是否足够好,或者是否有更清洁的方法来实现这一目标?

编辑:我不想使用 ISerializable 接口或其实现。我正在尝试读取二进制图像以计算出使用的字段并使用 PE 结构确定其数据。

4

2 回答 2

10

使用 P/Invoke marshaller 没有任何问题,它不是不安全的,您不必使用 unsafe 关键字。弄错只会产生错误的数据。它比显式编写反序列化代码更容易使用,尤其是当文件包含字符串时。您不能使用 BinaryReader.ReadString(),它假定字符串是由 BinaryWriter 写入的。但是请确保您使用 struct 声明来声明数据的结构,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 。

创建一个MemoryStreamfrom thebyte[]然后使用BinaryReaderfrom that。您应该能够读出结构并相应地填写您的对象。

于 2009-12-20T14:09:38.480 回答