托管和非托管结构的大小不同

时间:2010-01-09 00:56:57

标签: c# .net struct pinvoke marshalling

我正在通过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)。

添加的属性是否考虑到结构的大小并使其大于非托管版本?我会删除它们并看看会发生什么,但我的代码在很大程度上取决于它们,这使得这很困难。

我是否需要将StructLayoutAttributeSize属性一起使用?唯一令我困惑的是IntPtr。该库严格为32位,所以我可以继续安全地假设这些字段一直是32位吗?

2 个答案:

答案 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;
}