I've run into an odd scenario marshaling unions that contain arrays in C#/.NET. Consider the following program:
namespace Marshal
{
class Program
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct InnerType
{
byte Foo;
//[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 1)]
//byte[] Bar;
}
[StructLayout(LayoutKind.Explicit, Pack = 1)]
struct UnionType
{
[FieldOffset(0)]
InnerType UnionMember1;
[FieldOffset(0)]
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 1)]
byte[] UnionMember2;
}
static void Main(string[] args)
{
Console.WriteLine(@"SizeOf UnionType: {0}", System.Runtime.InteropServices.Marshal.SizeOf(typeof(UnionType)));
}
}
}
If you run this program, you'll get the following exception:
Could not load type 'UnionType' from assembly 'Marshal, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' because it contains an object field at offset 0 that is incorrectly aligned or overlapped by a non-object field.
Now if you uncomment the two commented out lines, the program will run fine. I'm wondering why this is. Why does adding an extra array to InnerType fix the problem? Incidentally, it doesn't matter what size you make the array. Without the array, UnionMember1 and UnionMember2 should match each other in size. With the array, their sizes do not match, yet no exceptions are thrown.
Update Changing InnerType to the following also causes the exception (on InnerType this time):
[StructLayout(LayoutKind.Explicit, Pack = 1)]
struct InnerType
{
[FieldOffset(0)]
byte Foo;
[FieldOffset(1)]
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 1)]
byte[] Bar;
}
It seems to me that this should be equivalent to the original code (with LayoutKind.Sequential
) where byte[] Bar
is uncommented.
I don't believe the problem here has anything to do with word boundaries--I'm using Pack = 1. Rather I think it's the second part of the exception, "...it contains an object field at offset 0 that is ... overlapped by a non-object field." byte[] is a reference type while byte itself is a value type. I can see that "byte Foo" will end up overlapping "byte[] UnionMember2". However, this still doesn't explain why uncommenting "byte[] bar" in my original code makes the exception go away.