I have the following .NET value types:
[StructLayout(LayoutKind.Sequential)]
public struct Date
{
public UInt16 V;
}
[StructLayout(LayoutKind.Sequential)]
public struct StringPair
{
public String A;
public String B;
public String C;
public Date D;
public double V;
}
I have code that is passing a pointer to a value type to unmanaged code, along with offsets discovered by calling System.Runtime.InteropServices.Marshal.OffsetOf. The unmanaged code is populating the Date and double values.
The offsets that are reported for the StringPair struct are exactly what I would expect: 0, 8, 16, 24, 32
I have the following code in a test function: FieldInfo[] fields = typeof(StringPair).GetFields(BindingFlags.Instance|BindingFlags.Public);
for ( int i = 0; i < fields.Length; i++ )
{
int offset = System.Runtime.InteropServices.Marshal.OffsetOf(typeof(StringPair), fields[i].Name).ToInt32();
Console.WriteLine(String.Format(" >> field {0} @ offset {1}", fields[i].Name, offset));
}
Which prints out exactly these offsets.
>> field A @ offset 0
>> field B @ offset 8
>> field C @ offset 16
>> field D @ offset 24
>> field V @ offset 32
I then have some test code: foreach (StringPair pair in pairs) { Date d = pair.D; double v = pair.V; ...
Which has the following assembler associated with it in the debugger:
Date d = pair.D;
0000035d lea rax,[rbp+20h]
00000361 add rax,20h
00000367 mov ax,word ptr [rax]
0000036a mov word ptr [rbp+000000A8h],ax
00000371 movzx eax,word ptr [rbp+000000A8h]
00000378 mov word ptr [rbp+48h],ax
double v = pair.V;
0000037c movsd xmm0,mmword ptr [rbp+38h]
00000381 movsd mmword ptr [rbp+50h],xmm0
It is loading the D field at offset 32 (0x20) and the V field at offset 24 (0x38-0x20). The JIT has changed the order around. The Visual Studio debugger shows this inverted order too.
Why!? I've been pulling my hair out try to see where my logic is going wrong. If I swap the order of D and V in the struct then everything works, but this code needs to be able to deal with a plugin architecture where other developers have defined the struct, and they can't be expected to remember arcane layout rules.