Marshal's alliance with arrays

I came across odd marshaling union scenarios 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 will 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 two missing lines, the program will work fine. I wonder why this is so. Why does adding an extra array to InnerType fix the problem? By the way, no matter what size you make in the array. Without an array, UnionMember1 and UnionMember2 must match each other in size. With the array, their sizes do not match, but exceptions are not thrown.

Change InnerType update the following as causes an exception (InnerType at 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 source code (with LayoutKind.Sequential ), where byte[] Bar uncommented.

I don’t think the problem here has anything to do with word boundaries β€” I use Pack = 1. Rather, I think this is the second part of the exception, "... it contains the field of the object at offset 0, which ... is overlapped by the field different from the object. " byte [] is a reference type, while the byte itself is a value type. I see that "byte Foo" will override "byte [] UnionMember2". However, this still does not explain why uncommenting the "byte [] bar" in my source code throws an exception.

+6
source share
1 answer

My hypothesis is that the sequential layout is canceled as described in this SO answer: LayoutKind.Sequential is not respected if the substructure has LayoutKind.Explicit .

Please note that the parameter of package 1 does not cancel the filling, i.e. Bar does not have a FieldOffset parameter of 1. It is aligned by 4 bytes (x32), and according to Marshal.OffsetOf() it should be 4 as expected.

However, the .NET runtime can actually put the reference type Bar before the Foo byte in managed memory, in which case it will correctly overlap when combined with UnionMember2 .

Interestingly, the same thing happens with Foo int and float, but with a long and double it gives an exception again. It seems to sort the fields by size, but first sets the types of links if the size is equal.

When I switched to x64, it also worked with long Foo , which would support this theory. Finally, I opened the memory window (Debug-> Windows-> Memory) and typed in the location &instance.UnionMember1.Foo and scrolled the bit until the bytes expanded to Foo . Then set the value to Foo and Bar, using the closest window, which proved that Bar is 0 and Foo is 4. (Add var instance = new UnionType() to Main )

Please keep in mind that this is probably not what you intended, Byte[] are considered to be only a reference type. You can replace it with object . You may be able to use fixed byte Bar[1] , depending on your goals.

0
source

All Articles