C# is a managed programming language which means everything by default (or 90% of time), everything is properly managed by the GC (Garbage Collector), you don’t have to worry about freeing resources. If you want to manage a structure in unsafe (non-managed) environment, you will need to use the functions in Marshal class (e.g. StructureToPtr).
Reading Data from Stream and Convert to Struct Type
If we have defined the following structure:
1 2 3 4 5 6 7 8 9 10 11 12 13 | [StructLayout(LayoutKind.Sequential, Pack = 4)] public struct TestStruct { [StructLayout(LayoutKind.Explicit)] public struct DataInfo { [FieldOffset(0)] public int X; [FieldOffset(0)] public float Y; } public DataInfo Data; public double Z; } |
[StructLayout(LayoutKind.Sequential, Pack = 4)] public struct TestStruct { [StructLayout(LayoutKind.Explicit)] public struct DataInfo { [FieldOffset(0)] public int X; [FieldOffset(0)] public float Y; } public DataInfo Data; public double Z; }
which is equivalent to the C/C++:
1 2 3 4 5 6 7 8 9 | #pragma pack(push, 4) struct { union { int X, float Y } Data; double Z; } #pragma pack(pop) |
#pragma pack(push, 4) struct { union { int X, float Y } Data; double Z; } #pragma pack(pop)
To obtain the size of the struct, we can use Marshal.SizeOf:
1 | int size = System.Runtime.InteropServices.Marshal.SizeOf(typeof(TestStruct)); |
int size = System.Runtime.InteropServices.Marshal.SizeOf(typeof(TestStruct));
To set the values:
1 2 3 4 5 6 | var data = new TestStruct() { Data = new TestStruct.DataInfo(), Z = 2 }; data.Data.X = 1; |
var data = new TestStruct() { Data = new TestStruct.DataInfo(), Z = 2 }; data.Data.X = 1;
We can then declare a class that can convert the data in MemoryStream to the specific type of structure.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | class MemoryReader : BinaryReader { public MemoryReader(byte[] buffer) : base(new MemoryStream(buffer)) { } public T ReadStruct<T>() { var byteLength = Marshal.SizeOf(typeof (T)); var bytes = ReadBytes(byteLength); var pinned = GCHandle.Alloc(bytes, GCHandleType.Pinned); var stt = (T) Marshal.PtrToStructure( pinned.AddrOfPinnedObject(), typeof (T)); pinned.Free(); return stt; } } |
class MemoryReader : BinaryReader { public MemoryReader(byte[] buffer) : base(new MemoryStream(buffer)) { } public T ReadStruct<T>() { var byteLength = Marshal.SizeOf(typeof (T)); var bytes = ReadBytes(byteLength); var pinned = GCHandle.Alloc(bytes, GCHandleType.Pinned); var stt = (T) Marshal.PtrToStructure( pinned.AddrOfPinnedObject(), typeof (T)); pinned.Free(); return stt; } }
Store Struct Data in Memory
We then can have a class that inherits from BinaryWriter that has the WriteStruct method which allows you to write the struct data to Memory Stream.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class MemoryWriter : BinaryWriter { public MemoryWriter(byte[] buffer) : base(new MemoryStream(buffer)) { } public void WriteStruct<T>(T t) { var sizeOfT = Marshal.SizeOf(typeof(T)); var ptr = Marshal.AllocHGlobal(sizeOfT); Marshal.StructureToPtr(t, ptr, false); var bytes = new byte[sizeOfT]; Marshal.Copy(ptr, bytes, 0, bytes.Length); Marshal.FreeHGlobal(ptr); Write(bytes); } } |
class MemoryWriter : BinaryWriter { public MemoryWriter(byte[] buffer) : base(new MemoryStream(buffer)) { } public void WriteStruct<T>(T t) { var sizeOfT = Marshal.SizeOf(typeof(T)); var ptr = Marshal.AllocHGlobal(sizeOfT); Marshal.StructureToPtr(t, ptr, false); var bytes = new byte[sizeOfT]; Marshal.Copy(ptr, bytes, 0, bytes.Length); Marshal.FreeHGlobal(ptr); Write(bytes); } }
Example Usage
1 2 3 4 5 6 7 8 9 10 11 12 | // writing and save data.Data.X = 1; var buffer = new byte[size]; var mem = new MemoryWriter(buffer); mem.WriteStruct(data); // read and load var buffer2 = new byte[size]; var mem2 = new MemoryReader(buffer); var newdata = mem2.ReadStruct<teststruct>(); // match Console.WriteLine(newdata.Z); Console.WriteLine(data.Data.X); |
// writing and save data.Data.X = 1; var buffer = new byte[size]; var mem = new MemoryWriter(buffer); mem.WriteStruct(data); // read and load var buffer2 = new byte[size]; var mem2 = new MemoryReader(buffer); var newdata = mem2.ReadStruct<teststruct>(); // match Console.WriteLine(newdata.Z); Console.WriteLine(data.Data.X);
C# does not support any type of fixed-length arrays that are embedded in a struct (except in a unsafe context). If you have something like this:
1 2 3 4 5 | struct TestStruct { MarshalAs(UnmanagedType.ByValArray, SizeConst = 100) BYTE[] UserID; }; |
struct TestStruct { MarshalAs(UnmanagedType.ByValArray, SizeConst = 100) BYTE[] UserID; };
The above won’t work and will throw out ArgumentException Error on WriteStruct method:
Error : Type could not be marshaled because the length of an embedded array instance does not match the declared length in the layout
–EOF (The Ultimate Computing & Technology Blog) —
loading...
Last Post: Calculate the Number of Boundary Cubes and Boundary Squares of a NxN Rubik Cube
Next Post: Ping when VPS/Dedicate Server is Restarting