C# array within a struct

Use fixed size buffers:

[StructLayout(LayoutKind.Explicit)]
unsafe struct headerUnion                  // 2048 bytes in header
{
    [FieldOffset(0)]
    public fixed byte headerBytes[2048];      
    [FieldOffset(0)]
    public headerLayout header; 
}

Alternativ you can just use the struct and read it with the following extension method:

private static T ReadStruct<T>(this BinaryReader reader)
        where T : struct
{
    Byte[] buffer = new Byte[Marshal.SizeOf(typeof(T))];
    reader.Read(buffer, 0, buffer.Length);
    GCHandle handle = default(GCHandle);
    try
    {
        handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
        return (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
    }
    finally
    {
        if (handle.IsAllocated) 
            handle.Free();
    }
}

Unmanaged structures can contain embedded arrays. By default, these embedded array fields are marshaled as a SAFEARRAY. In the following example, s1 is an embedded array that is allocated directly within the structure itself.

Unmanaged representation
struct MyStruct {
    short s1[128];
}

Arrays can be marshaled as UnmanagedType.ByValArray, which requires you to set the MarshalAsAttribute.SizeConst field. The size can be set only as a constant. The following code shows the corresponding managed definition of MyStruct. C#VB

[StructLayout(LayoutKind.Sequential)]
public struct MyStruct {
   [MarshalAs(UnmanagedType.ByValArray, SizeConst=128)] public short[] s1;
}

I wouldn't use that pattern in the first place. This kind of memory mapping may be appropriate in c, but not in a high level language like C#.

I'd just write a call to the binary reader for each member I want to read. This means you can use classes and write them in a clean high level way.

It also takes care of endian issues. Whereas memory mapping will break when used on different endian systems.

Related question: Casting a byte array to a managed structure


So your code would look similar to the following (add access modifiers etc.):

class Record
{
    char[] name;
    int dt1;
}
class Block {
    char[] version;
    int  field1;
    int  field2;
    RECORD[] records;
    char[] filler1;
}

class MyReader
{
    BinaryReader Reader;

    Block ReadBlock()
    {
        Block block=new Block();
        block.version=Reader.ReadChars(4);
        block.field1=Reader.ReadInt32();
        block.field2=Reader.ReadInt32();
        block.records=new Record[15];
        for(int i=0;i<block.records.Length;i++)
            block.records[i]=ReadRecord();
        block.filler1=Reader.ReadChars(24);
        return block;
    }

    Record ReadRecord()
    {
        ...
    }

    public MyReader(BinaryReader reader)
    {
        Reader=reader;
    }
}

Tags:

C#