我在c api和.net应用程序之间写了一些胶水代码。为了编写C#结构,我需要弄清楚c侧实际发生了什么。
typedef struct CommonDialogBaseParam {
size_t size;
uint8_t reserved[36];
uint32_t magic;
} CommonDialogBaseParam __attribute__ ((__aligned__(8)));
// Somewhere else
#define __attribute__(x)
我很想写下C#等价物如下:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct CommonDialogBaseParam {
public ulong size;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=36)]
public string reserved;
public uint magic;
}
毋庸置疑......我真的不需要在C#端正确访问'reserved'或'magic',但我需要正确保存成员。
答案 0 :(得分:1)
这是一个GCC属性,它指定结构的最小对齐为8个字节。可以在此处找到文档:https://gcc.gnu.org/onlinedocs/gcc/Common-Type-Attributes.html#Common-Type-Attributes
这会影响这些结构的分配,但它不会影响结构本身的偏移。但是,它确实意味着本机结构可能在最终成员之后有额外的填充。
在C#中没有与此相同的内容,并且根据本机代码对结构的作用,您可能会遇到问题。如果由C#代码分配,结构可能会错位,但这只会影响性能而不是正确性。如果本机代码生成结构的副本,那么它将尝试读取结构末尾的任何填充。可以想象,这可能会导致访问违规。我的猜测是p / invoke使用的堆分配器将以块大小至少为8的块分配内存,所以你可能会侥幸逃脱。但我怀疑你能不能依赖它。
将这个填充放在一边,我会翻译你的结构:
[StructLayout(LayoutKind.Sequential)]
public struct CommonDialogBaseParam {
public UIntPtr size;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=36)]
public byte[] reserved;
public uint magic;
}
请注意,我已将size_t
映射到UIntPtr
,这是一个无符号指针大小的值。我个人更喜欢看byte[]
字段为reserved
。就我所知,这对C代码更为真实。
如果你想确定你的结构足够大,那么你可以在最后添加一个额外的字段:
[StructLayout(LayoutKind.Sequential)]
public struct CommonDialogBaseParam {
public UIntPtr size;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=36)]
public byte[] reserved;
public uint magic;
private uint padding;
}
由于此结构中包含的任何类型都没有大于8的大小,因此您可以滥用Pack
的{{1}}选项来达到预期的效果:
StructLayout
但要小心这一点。本机代码对齐结构。上面的C#声明对齐struct,并指定其成员的对齐方式。在这里发生的事情是,这不会影响成员对齐,但情况并非总是如此。
总而言之,有点令人不满意。