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:
[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++:
#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:
int size = System.Runtime.InteropServices.Marshal.SizeOf(typeof(TestStruct));
To set the values:
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.
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.
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
// 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:
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) —
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