I have the following .NET value types:
[StructLayo开发者_JS百科ut(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.
If you need explicit layout... use explicit layout...
[StructLayout(LayoutKind.Explicit)]
public struct StringPair
{
[FieldOffset(0)] public String A;
[FieldOffset(8)] public String B;
[FieldOffset(16)] public String C;
[FieldOffset(24)] public Date D;
[FieldOffset(32)] public double V;
}
The info you get from the Marshal class is only relevant if the type actually gets marshaled. The internal memory layout of a managed structure is not discoverable through any documented means, other than peeking at the assembly code perhaps.
Which means the CLR is free to reorganize the layout of the structure and optimize the packing. Swapping the D and V fields makes your structure smaller due the alignment requirements of a double. It saves 6 bytes on your 64-bit machine.
Not sure why this would be an issue for you, it shouldn't be. Consider Marshal.StructureToPtr() to get the structure laid-out the way you want it.
Two things:
StructLayout(Sequential)
does not guarantee packing. You might want to usePack=1
, otherwise 32 and 64bit platforms might be different.and string is a reference, not a pointer. If the string length is always fixed, you might want to use fixed char arrays:
public struct MyArray // This code must appear in an unsafe block { public fixed char pathName[128]; }
精彩评论