我正在通过P / Invoke使用非托管库,它使用三个结构(虽然它们都有相同的基本布局,所以我只发布一个):
struct Agraph_t {
int tag:4;
int kind:4;
int handle:24;
char **attr;
char *didset;
char *name;
Agdata_t *univ;
Dict_t *nodes, *inedges, *outedges;
Agraph_t *root;
Agnode_t *meta_node;
Agproto_t *proto;
Agraphinfo_t u;
};
由于我的包装器使用这些对象的方式,我必须将Agraph_t
内的结构称为IntPtr
s。我添加了一些属性,可以更轻松地访问位字段的值。
public struct Agraph_t {
public uint tag_kind_handle;
public IntPtr attr;
public string didset;
public string name;
public IntPtr univ;
public IntPtr nodes, inedges, outedges;
public IntPtr root;
public IntPtr meta_node;
public IntPtr proto;
public IntPtr u;
public uint Tag {
get { return (tag_kind_handle & 15u); }
}
public uint Kind {
get { return (tag_kind_handle & 240u) / 16; }
}
public uint Handle {
get { return (tag_kind_handle & 4294967040u) / 256; }
}
}
在执行任何操作之前,我必须通过为非托管库提供三个结构中每个结构的大小来初始化它。
aginitlib(Marshal.SizeOf(typeof(Agraph_t)), ..., ...);
执行此操作时我没有收到错误,我可以正常使用该库。但是,库的一部分使用非托管结构的大小自行调用aginitlib(我无法控制)。那时,图书馆警告我它已经用两种不同的大小初始化,这使它变得不稳定(在某些操作后抛出AccessViolationException
)。
添加的属性是否考虑到结构的大小并使其大于非托管版本?我会删除它们并看看会发生什么,但我的代码在很大程度上取决于它们,这使得这很困难。
我是否需要将StructLayoutAttribute
与Size
属性一起使用?唯一令我困惑的是IntPtr
。该库严格为32位,所以我可以继续安全地假设这些字段一直是32位吗?
答案 0 :(得分:1)
IntPtr在32位代码中为4字节宽,在64位模式下为8字节长。
您可以使用IntPtr.Size属性确定大小,该属性报告当前运行时的值的大小。
至于你的另一个问题,你应该真正使用StructLayoutAttribute
来确保托管和非托管结构以相同的方式布局在内存中,具有相同的填充和字段大小。
您还需要从结构中删除属性,因为它们会影响内存中结构的大小。
答案 1 :(得分:1)
不同之处在于u
的声明。非托管声明有这个:
Agraphinfo_t u;
这意味着Agraphinfo_t在Agraph_t结构中内联分配。如果Agraphinfo_t的大小为16个字节,则它为sizeof(Agraph_t)贡献16个字节。
但是,在您的托管声明中,您声明如下:
public IntPtr u;
这意味着在Agraph_t结构中分配了指针。在32位系统上,这将为sizeof(Agraph_t)贡献4个字节。因此,为Agraph_t计算的两个大小不同步。
要解决此问题,请声明Agraphinfo_t的托管等效项,并在Agraph_t中创建该实例:
public struct Agraphinfo_t
{
// fields go here as per unmanaged definition
}
public struct Agraph_t
{
// ....
public Agraphinfo_t u;
}