tags:

views:

362

answers:

4

Can anyone tell me how to get a array of bytes into a structure in a direct fashion in C# .NET version 2? Like the familiar fread as found in C, so far I have not had much success in reading a stream of bytes and automatically filling a structure. I have seen some implementations where there is pointer hocus-pocus in the managed code by using the unsafe keyword.

Have a look at this sample:

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

The reason I have two constructors in foobarStruct

  • Is there cannot be an empty constructor.
  • Pass in a block of memory (as a byte array) into the constructor when instantiating the structure.

Is that implementation good enough or is there a much cleaner way to achieve this?

Edit: I do not want to use the ISerializable interface or its implementation. I am trying to read a binary image to work out the fields used and determine its data using the PE structures.

A: 

Have a look here.

http://www.switchonthecode.com/tutorials/csharp-tutorial-serialize-objects-to-a-file

Daniel A. White
@Daniel: Thanks for your input - I have edited my question accordingly.. :) Thanks again.
tommieb75
A: 

Here is good thread you might be interested in

Captain Comic
+2  A: 

You probably want to use a BinaryReader which allows you to read in primitive types in binary form.

Create a MemoryStream from the byte[] and then use the BinaryReader from that. You should be able to read out the structure and fill in your object accordingly.

Jeff Foster
+4  A: 

There isn't anything wrong with using the P/Invoke marshaller, it is not unsafe and you don't have to use the unsafe keyword. Getting it wrong will just produce bad data. It can be a lot easier to use than explicitly writing the deserialization code, especially when the file contains strings. You can't use BinaryReader.ReadString(), it assumes that the string was written by BinaryWriter. Make sure however that you declare the structure of the data with a struct declaration, this.GetType() is not likely to work out well.

Here's a generic class that will make it work for any structure declaration:

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

A sample declaration for the structure of the data in the file:

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

You'll need to tweak the structure declaration and the attributes to get a match with the data in the file. Sample code that reads a file:

  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));
    }
  }
Hans Passant