Управляемая и неуправляемая структура не имеют одинаковый размер - PullRequest
1 голос
/ 09 января 2010

Я работаю с неуправляемой библиотекой через 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 s после определенных операций).

Добавлены ли добавленные свойства в размер структуры и делают ее больше, чем неуправляемая версия? Я бы удалил их и посмотрел, что произойдет, но мой код сильно зависит от них, что делает это трудным.

Нужно ли использовать StructLayoutAttribute со свойством Size? Единственное, что меня смущает, это IntPtr s. Библиотека строго 32-битная, так что я могу пойти дальше и смело предположить, что эти поля всегда будут 32-битными?

Ответы [ 2 ]

1 голос
/ 09 января 2010

Разница из-за объявления для u. Неуправляемая декларация имеет следующее:

Agraphinfo_t u;

Это означает, что Agraphinfo_t размещается встроенным в структуре Agraph_t. Если Agraphinfo_t, скажем, имеет размер 16 байт, то он добавляет 16 байтов к sizeof (Agraph_t).

Однако в своей управляемой декларации вы заявляете, что вы так:

public IntPtr u;

Это означает, что указатель размещен в структуре Agraph_t. В 32-битной системе это даст 4 байта для sizeof (Agraph_t). Следовательно, два размера, вычисленные для 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;
}
1 голос
/ 09 января 2010

IntPtr имеет ширину 4 байта в 32-битном коде и длину 8 байтов в 64-битном режиме.

Размер можно определить с помощью свойства IntPtr.Size, которое сообщает размер значения для текущей среды выполнения.

Что касается вашего другого вопроса, вам действительно следует использовать StructLayoutAttribute, чтобы гарантировать, что управляемые и неуправляемые структуры будут размещены в памяти одинаково, с одинаковыми размерами полей и полей.

Вам также потребуется удалить свойства из структуры, поскольку они будут влиять на размер структуры в памяти.

...