Com 互操作结构定义与内存布局不匹配
Com Interop Structure Definition Does Not Match Memory Layout
COM 中定义了一个结构 header (vds.h):
typedef struct _VDS_DISK_EXTENT
{
// A Guid
VDS_OBJECT_ID diskId;
// An Enum, with the largest defined value 0x7FFF
VDS_DISK_EXTENT_TYPE type;
ULONGLONG ullOffset;
ULONGLONG ullSize;
// Guid
VDS_OBJECT_ID volumeId;
//Guid
VDS_OBJECT_ID plexId;
ULONG memberIdx;
} VDS_DISK_EXTENT;
我已经注释了一些字段的类型。根据此 header 定义,该结构似乎有 72 个字节大。但是,当我在 C# 中编组它时(我得到一个 IntPtr 到这些结构的数组)并查看 IntPtr 指示的内存,我看到了:
0x01717A50 a8 c5 af 28 37 e1 0d 43 -> diskId
0x01717A58 b0 87 e2 ef 94 5f 9f 27 -> diskId
0x01717A60 02 00 00 00 00 00 00 00 -> first 4 bytes are extent type, second 4?
0x01717A68 00 00 70 23 00 00 00 00 -> offset
0x01717A70 00 00 50 4d 74 00 00 00 -> size
0x01717A78 38 3c 22 26 e9 de df 44 -> volumeId
0x01717A80 81 f3 ba ee af e2 ad 2b -> volumeId
0x01717A88 48 98 78 bb 7f dd bc 41 -> plexId
0x01717A90 94 17 db d2 86 01 54 ce -> plexId
0x01717A98 00 00 00 00 00 00 00 00 -> first 4 bytes are membderIdx, second 4?
如您所见,有2个4字节的区域未被计算在内。 C++ 枚举声明为 typedef enum _VDS_DISK_EXTENT_TYPE {
所以我不认为它是 8 个字节。与 memberIdx 相同,它被声明为一个 ULONG,所以它是 4 个字节。这个填充在哪里定义或者每个字段的偏移量在哪里定义?如果我必须查看内存中的每一个并弄清楚字段之间的真正边界在哪里,那么几乎不可能使用此 com 程序集中的 objects。
这看起来像是结构成员对齐的情况。具体来说,这个结构是用 8 字节的对齐方式编译的,这是 Visual C++ 的默认值。请注意,每个成员都以 8 字节为边界开始。如果有小于 8 字节的连续成员(例如,两个连续的 ULONG
s),您将不会在第一个 ULONG
上看到额外的填充(因为 4 字节的成员可以对齐使用对齐值 >= 4 时在 4 字节边界上)。但是因为你有一个 ULONG
后跟一个 ULONGLONG
,所以 ULONG
成员被填充以便后面的 ULONGLONG
成员在 8 字节边界上对齐(因为8 字节成员不能在 4 字节边界上对齐)。
COM 中定义了一个结构 header (vds.h):
typedef struct _VDS_DISK_EXTENT
{
// A Guid
VDS_OBJECT_ID diskId;
// An Enum, with the largest defined value 0x7FFF
VDS_DISK_EXTENT_TYPE type;
ULONGLONG ullOffset;
ULONGLONG ullSize;
// Guid
VDS_OBJECT_ID volumeId;
//Guid
VDS_OBJECT_ID plexId;
ULONG memberIdx;
} VDS_DISK_EXTENT;
我已经注释了一些字段的类型。根据此 header 定义,该结构似乎有 72 个字节大。但是,当我在 C# 中编组它时(我得到一个 IntPtr 到这些结构的数组)并查看 IntPtr 指示的内存,我看到了:
0x01717A50 a8 c5 af 28 37 e1 0d 43 -> diskId
0x01717A58 b0 87 e2 ef 94 5f 9f 27 -> diskId
0x01717A60 02 00 00 00 00 00 00 00 -> first 4 bytes are extent type, second 4?
0x01717A68 00 00 70 23 00 00 00 00 -> offset
0x01717A70 00 00 50 4d 74 00 00 00 -> size
0x01717A78 38 3c 22 26 e9 de df 44 -> volumeId
0x01717A80 81 f3 ba ee af e2 ad 2b -> volumeId
0x01717A88 48 98 78 bb 7f dd bc 41 -> plexId
0x01717A90 94 17 db d2 86 01 54 ce -> plexId
0x01717A98 00 00 00 00 00 00 00 00 -> first 4 bytes are membderIdx, second 4?
如您所见,有2个4字节的区域未被计算在内。 C++ 枚举声明为 typedef enum _VDS_DISK_EXTENT_TYPE {
所以我不认为它是 8 个字节。与 memberIdx 相同,它被声明为一个 ULONG,所以它是 4 个字节。这个填充在哪里定义或者每个字段的偏移量在哪里定义?如果我必须查看内存中的每一个并弄清楚字段之间的真正边界在哪里,那么几乎不可能使用此 com 程序集中的 objects。
这看起来像是结构成员对齐的情况。具体来说,这个结构是用 8 字节的对齐方式编译的,这是 Visual C++ 的默认值。请注意,每个成员都以 8 字节为边界开始。如果有小于 8 字节的连续成员(例如,两个连续的 ULONG
s),您将不会在第一个 ULONG
上看到额外的填充(因为 4 字节的成员可以对齐使用对齐值 >= 4 时在 4 字节边界上)。但是因为你有一个 ULONG
后跟一个 ULONGLONG
,所以 ULONG
成员被填充以便后面的 ULONGLONG
成员在 8 字节边界上对齐(因为8 字节成员不能在 4 字节边界上对齐)。