There is nothing wrong with using the P / Invoke marshaller, it is unsafe, and you do not need to use the unsafe keyword. If you are mistaken, this will lead to bad data. It can be a lot easier to use than explicitly writing deserialization code, especially when the file contains lines. You cannot use BinaryReader.ReadString (), it assumes the string was written by BinaryWriter. However, make sure you declare the data structure with a struct declaration. This.GetType () is unlikely to work well.
Here is a generic class that will make it work to declare any structure:
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; }
An example declaration for the data structure in a file:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] struct Sample { [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 42)] public string someString; }
You will need to configure the declaration of the structure and attributes in order to match the data in the file. Example 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
source share