1

I am trying to serialise a struct that has a the following layout:

[StructLayout(LayoutKind.Explicit, Pack = 2)]
public readonly struct EntryInfo
{
    [FieldOffset(0x00)]
    [MarshalAs(UnmanagedType.U2, SizeConst = 2)]
    public readonly ushort Type;

    [FieldOffset(0x02), MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
    public readonly string Name;
}

Sadly, even though Pack=2 and MarshalAs(UnmanagedType.U2) is specified, it still fails at runtime with:

System.TypeLoadException: Could not load type 'Structs.EntryInfo' from assembly 'MySharp, 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.

The goal is keep using explicit field offsets (hence requiring Explicit layout type), yet still support packed/unaligned fields.

Setting the layout to Sequential and keeping the Pack option makes it work, but I have some structs where the data I know how to read starts at not at the beginning of the struct, hence I'd like to have to avoid having to use a sequential layout and creating padding fields.

If I set the layout to Sequential and keep the Pack field, it all works, but I can't really see why the current code should not work, namely, I have specified the correct managed types, I did set the pack.

Based on Incorrectly-aligned/non-object field in struct I understand that the array should be DWORD aligned, but it does not explain why that is the case, or why that requirement goes away with Sequential mode.

Jammer
  • 542
  • 1
  • 6
  • 14
  • Does this answer your question? [Incorrectly-aligned/non-object field in struct](https://stackoverflow.com/questions/6891867/incorrectly-aligned-non-object-field-in-struct) – Charlieface Nov 18 '21 at 09:29
  • `Pack` does nothing for structs with `LayoutKind.Explicit`, since, well, you're laying things out explicitly. If you want things packed, that's up to you. Nor does the `MarshalAs` attribute on the `ushort` do anything meaningful since serializing it as a 2-byte value would be the default anyway. The issue is explained in the linked question: marshaling a `string` as a value not aligned to a pointer is blocked; you'll see the error still if you remove `Type` entirely. By far the best approach is to not use the marshaler as a generic binary serializer; it's relatively slow and inflexible. – Jeroen Mostert Nov 18 '21 at 15:12
  • I'd like to understand where the limitation comes from, and if there is a workaround, clearly if sequential is able to do it, I should be able to do it with explicit? i know I can read everything as bytes and hand roll the unmarshalling, but that's not great from usability perspective. I assume you are suggesting that sequential is not doing what I think it is? If C++ is able to use a struct like this, why is C# not able? – Jammer Nov 19 '21 at 01:13

0 Answers0