Search code examples
c#structunionmarshalling

Fixed size byte array with fixed offset inside manually marshalled struct throws runtime exception?


Target is to have the following struct working in C# (.net 8.0)

    [StructLayout(LayoutKind.Explicit, Size = 257, Pack = 1)]
    public struct MyFrame
    {
      [FieldOffset(0)] public byte valueA;
      [FieldOffset(1)] public byte valueB;
      [MarshalAs(UnmanagedType.ByValArray, SizeConst = 254)]
      [FieldOffset(2)] public byte[] valueArray;
      [FieldOffset(256)] public byte valueC;
    }

Program compiles but throws error message during initialisation.

System.TypeLoadException HResult=0x80131522 Message=Could not load type 'MyFrame' from assembly 'MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' because it contains an object field at offset 2 that is incorrectly aligned or overlapped by a non-object field. Source=MyAssemböy StackTrace: at MyAssembly.MyClass..ctor() ...

My understanding was, that using a pack size of 1 this should be possible. It compiles and runs, if i move valueArray to start at offset 8, but i want to have it at 2.

Ideally I would also like to extend it union like to this

    [StructLayout(LayoutKind.Explicit, Size = 258, Pack = 1)]
    public struct MyFrame
    {
      [FieldOffset(0)] public byte valueA;
      [FieldOffset(1)] public byte valueB;
      [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1,  SizeConst = 254)]
      [FieldOffset(2)] public byte[] valueArray;
      [FieldOffset(256)] public byte valueC;
      [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1,  SizeConst = 257)]
      [FieldOffset(0)] public byte[] rawBytes;
    }

How can I solve this?


Solution

  • You can't have overlapping reference-type objects, so you can't have the array overlapping anything else. Overlapping is an issue in the managed version, rather than the unmanaged version.

    It would overlap here because the FieldOffset affects the managed version as well.

    But you don't need to use LayoutKind.Explicit, you can use Sequential

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct MyFrame
    {
        public byte valueA;
        public byte valueB;
        [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 254)]
        public byte[] valueArray;
        public byte valueC;
    }