Некоторые вопросы P / Invoke C # to C по работе с логическими значениями в структурах - PullRequest
2 голосов
/ 07 июля 2011

У меня есть некоторые проблемы при работе с булевыми типами и их распределении в структуре между C # и C. Я очень ржавый в C, но, надеюсь, в этой части нет ничего принципиально неправильного.

Насколько я читал / видел, тип .NET Boolean и C # bool имеет длину 4 байта , а тип C bool составляет всего 1 байт .По причинам, связанным с объемом памяти, я не могу использовать определенную версию BOOL 4 байта в коде C .

Вот простой тестовый код, который, надеюсь, прояснит мои вопросы:


Код C:

typedef struct
{
        double SomeDouble1;
        double SomeDouble2;
        int SomeInteger;
        bool SomeBool1;
        bool SomeBool2;
} TestStruct;

extern "C" __declspec(dllexport) TestStruct* __stdcall TestGetBackStruct(TestStruct* structs);

__declspec(dllexport) TestStruct* __stdcall TestGetBackStruct(TestStruct* structs)
{
    return structs;
}

Я называю этот код на C #используя следующие определения:

    [StructLayout(LayoutKind.Explicit)]
    public struct TestStruct
    {
        [FieldOffset(0)]
        public double SomeDouble1;
        [FieldOffset(8)]
        public double SomeDouble2;

        [FieldOffset(16)]
        public int SomeInteger;

        [FieldOffset(17), MarshalAs(UnmanagedType.I1)]
        public bool SomeBool1;
        [FieldOffset(18), MarshalAs(UnmanagedType.I1)]
        public bool SomeBool2;
    };

    [DllImport("Front.dll", CharSet = CharSet.Auto, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
    public static extern IntPtr TestGetBackStruct([MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] TestStruct[] structs);

и вот фактическая тестовая функция в C #:

    [Test]
    public void Test_CheckStructParsing()
    {
        var theStruct = new TestStruct();
        theStruct.SomeDouble1 = 1.1;
        theStruct.SomeDouble2 = 1.2;
        theStruct.SomeInteger = 1;
        theStruct.SomeBool1 = true;
        theStruct.SomeBool2 = false;

        var structs = new TestStruct[] { theStruct };

        IntPtr ptr = TestGetBackStruct(structs);

        var resultStruct = (TestStruct)Marshal.PtrToStructure(ptr, typeof(TestStruct));
    }

Это работает в том смысле, что Я получаю структуру обратно (используя отладчик для проверки), , но с совершенно неправильными значениями .Т.е. сортировка не работает вообще.Я пробовал другую версию структуры C # без успеха.Итак, вот мои вопросы (1 и 2 наиболее важные):

  1. Корректна ли функция C для этой цели?
  2. Как структурабыть написанным правильно, чтобы вернуть мне правильные значения в структуре обратно в C #? (необходимо ли даже определять структуру с помощью атрибута StructLayout (LayoutKind.Explicit), используя значения FieldOffset, или я могу использовать StructLayout (LayoutKind.Sequential) вместо)?
  3. Поскольку я возвращаю указатель на TestStruct в C, я думаю, что должна быть возможность вернуть массив TestStructs в C # .Но это не представляется возможным при использовании функции Marshal.PtrToStructure.Будет ли это возможно каким-то другим способом?
  4. Очевидно, что в C можно использовать то, что называется объединениями, если несколько структурных полей указывают на одно и то же выделение памяти с использованием одного и того же значения атрибута FieldOffset.Я понимаю это, но я все еще еще не понимаю, когда такой сценарий будет полезен .Пожалуйста, просветите меня.
  5. Может ли кто-нибудь порекомендовать хорошую книгу по C # P / Invoke для C / C ++? Я немного устаю получать кое-какую информацию здесь и там наИнтернет.

Большое спасибо за помощь по этим вопросам.Надеюсь, их было не так уж много.

1 Ответ

6 голосов
/ 07 июля 2011

Прекратите использовать LayoutKind.Explicit и избавьтесь от атрибутов FieldOffset, и ваш код будет работать. Ваши смещения не правильно совмещали поля.

public struct TestStruct
{
    public double SomeDouble1;
    public double SomeDouble2;
    public int SomeInteger;
    [MarshalAs(UnmanagedType.I1)]
    public bool SomeBool1;
    [MarshalAs(UnmanagedType.I1)]
    public bool SomeBool2;
};

Объявите функцию в C # следующим образом:

public static extern void TestGetBackStruct(TestStruct[] structs);

Маршаллинг по умолчанию будет соответствовать вашей декларации C ++ (на самом деле ваш код - C ++, а не C), но вы должны убедиться, что вы выделили параметр TestStruct[] в коде C # до вызова функции. Обычно вы также передаете длину массива в качестве параметра, чтобы код C ++ знал, сколько существует структур.

Пожалуйста, не пытайтесь вернуть массив структур из функции. Используйте параметр structs в качестве параметра ввода / вывода.

Я не знаю ни одной книги с акцентом на P / Invoke. Похоже, что-то черное искусство!

...