Что я делаю не так с этим использованием StructLayout( LayoutKind.Explicit ) при вызове структуры PInvoke с объединением?

StackOverflow https://stackoverflow.com/questions/1602487

Вопрос

Ниже приведена полная программа.Это работает нормально до тех пор, пока вы не раскомментируете '#define BROKEN' вверху.Перерыв произошел из-за того, что PInvoke не смог правильно организовать объединение.Тот самый INPUT_RECORD рассматриваемая структура имеет несколько подструктур, которые могут быть использованы в зависимости от значения в EventType.

Чего я не понимаю, так это того, что когда я определяю только единственную дочернюю структуру KEY_EVENT_RECORD это работает с явным объявлением при смещении 4.Но когда я добавляю другие структуры с тем же смещением, содержимое структуры полностью закрывается из шланга.

//UNCOMMENT THIS LINE TO BREAK IT:
//#define BROKEN

using System;
using System.Runtime.InteropServices;

class ConIOBroken
{
    static void Main()
    {
        int nRead = 0;
        IntPtr handle = GetStdHandle(-10 /*STD_INPUT_HANDLE*/);
        Console.Write("Press the letter: 'a': ");

        INPUT_RECORD record = new INPUT_RECORD();
        do
        {
            ReadConsoleInputW(handle, ref record, 1, ref nRead);
        } while (record.EventType != 0x0001/*KEY_EVENT*/);

        Assert.AreEqual((short)0x0001, record.EventType);
        Assert.AreEqual(true, record.KeyEvent.bKeyDown);
        Assert.AreEqual(0x00000000, record.KeyEvent.dwControlKeyState & ~0x00000020);//strip num-lock and test
        Assert.AreEqual('a', record.KeyEvent.UnicodeChar);
        Assert.AreEqual((short)0x0001, record.KeyEvent.wRepeatCount);
        Assert.AreEqual((short)0x0041, record.KeyEvent.wVirtualKeyCode);
        Assert.AreEqual((short)0x001e, record.KeyEvent.wVirtualScanCode);
    }

    static class Assert { public static void AreEqual(object x, object y) { if (!x.Equals(y)) throw new ApplicationException(); } }

    [DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern IntPtr GetStdHandle(int nStdHandle);

    [DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern bool ReadConsoleInputW(IntPtr hConsoleInput, ref INPUT_RECORD lpBuffer, int nLength, ref int lpNumberOfEventsRead);

    [StructLayout(LayoutKind.Explicit)]
    public struct INPUT_RECORD
    {
        [FieldOffset(0)]
        public short EventType;
        //union {
        [FieldOffset(4)]
        public KEY_EVENT_RECORD KeyEvent;
#if BROKEN
        [FieldOffset(4)]
        public MOUSE_EVENT_RECORD MouseEvent;
        [FieldOffset(4)]
        public WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent;
        [FieldOffset(4)]
        public MENU_EVENT_RECORD MenuEvent;
        [FieldOffset(4)]
        public FOCUS_EVENT_RECORD FocusEvent;
        //}
#endif
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct KEY_EVENT_RECORD
    {
        public bool bKeyDown;
        public short wRepeatCount;
        public short wVirtualKeyCode;
        public short wVirtualScanCode;
        public char UnicodeChar;
        public int dwControlKeyState;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct MOUSE_EVENT_RECORD
    {
        public COORD dwMousePosition;
        public int dwButtonState;
        public int dwControlKeyState;
        public int dwEventFlags;
    };

    [StructLayout(LayoutKind.Sequential)]
    public struct WINDOW_BUFFER_SIZE_RECORD
    {
        public COORD dwSize;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct MENU_EVENT_RECORD
    {
        public int dwCommandId;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct FOCUS_EVENT_RECORD
    {
        public bool bSetFocus;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct COORD
    {
        public short X;
        public short Y;
    }
}

Обновить:

Для тех, кто беспокоится о самих объявлениях структуры:

  1. bool обрабатывается как 32-битное значение
  2. причина смещения (4) для данных заключается в том, чтобы разрешить выравнивание 32-разрядной структуры, которое предотвращает начало объединения со смещением 2.

Опять же, моя проблема вообще не в том, чтобы заставить PInvoke работать, она пытается выяснить, почему эти дополнительные структуры (предположительно с одинаковым смещением) собирают данные, просто добавляя их.

Это было полезно?

Решение


//UNCOMMENT THIS LINE TO BREAK IT:
//#define BROKEN

использование Системы;использование System.Runtime.Службы взаимодействия;

класс Coniobrokened { статическая пустота Main() { int nRead = 0;Дескриптор IntPtr = GetStdHandle(-10 /STD_INPUT_HANDLE/);Консоль.Написать("Нажмите на букву:"а":");

    INPUT_RECORD record = new INPUT_RECORD();
    do
    {
        ReadConsoleInputW(handle, ref record, 1, ref nRead);
    } while (record.EventType != 0x0001/*KEY_EVENT*/);

    Assert.AreEqual((short)0x0001, record.EventType);
    Assert.AreEqual(1u, record.KeyEvent.bKeyDown);
    Assert.AreEqual(0x00000000, record.KeyEvent.dwControlKeyState & ~0x00000020);//strip num-lock and test
    Assert.AreEqual('a', record.KeyEvent.UnicodeChar);
    Assert.AreEqual((short)0x0001, record.KeyEvent.wRepeatCount);
    Assert.AreEqual((short)0x0041, record.KeyEvent.wVirtualKeyCode);
    Assert.AreEqual((short)0x001e, record.KeyEvent.wVirtualScanCode);
    return;
}

static class Assert { public static void AreEqual(object x, object y) { if (!x.Equals(y)) throw new ApplicationException(); } }

[DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr GetStdHandle(int nStdHandle);

[DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool ReadConsoleInputW(IntPtr hConsoleInput, ref INPUT_RECORD lpBuffer, int nLength, ref int lpNumberOfEventsRead);

[StructLayout(LayoutKind.Explicit)]
public struct INPUT_RECORD
{
    [FieldOffset(0)]
    public short EventType;
    //union {
    [FieldOffset(4)]
    public KEY_EVENT_RECORD KeyEvent;
    [FieldOffset(4)]
    public MOUSE_EVENT_RECORD MouseEvent;
    [FieldOffset(4)]
    public WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent;
    [FieldOffset(4)]
    public MENU_EVENT_RECORD MenuEvent;
    [FieldOffset(4)]
    public FOCUS_EVENT_RECORD FocusEvent;
}

[StructLayout(LayoutKind.Sequential)]
public struct KEY_EVENT_RECORD
{
    public uint bKeyDown;
    public short wRepeatCount;
    public short wVirtualKeyCode;
    public short wVirtualScanCode;
    public char UnicodeChar;
    public int dwControlKeyState;
}

[StructLayout(LayoutKind.Sequential)]
public struct MOUSE_EVENT_RECORD
{
    public COORD dwMousePosition;
    public int dwButtonState;
    public int dwControlKeyState;
    public int dwEventFlags;
};

[StructLayout(LayoutKind.Sequential)]
public struct WINDOW_BUFFER_SIZE_RECORD
{
    public COORD dwSize;
}

[StructLayout(LayoutKind.Sequential)]
public struct MENU_EVENT_RECORD
{
    public int dwCommandId;
}

[StructLayout(LayoutKind.Sequential)]
public struct FOCUS_EVENT_RECORD
{
    public uint bSetFocus;
}

[StructLayout(LayoutKind.Sequential)]
public struct COORD
{
    public short X;
    public short Y;
}

}

Другие советы

Я считаю, что это сработает, если вы сделаете bSetFocus и dwCommandId типа uint.

В будущем ознакомьтесь с PInvoke вики за надлежащими подписями.Обычно это точная или, по крайней мере, хорошая отправная точка.

1) общедоступный bool bKeyDown должен быть Int32 bKeyDown, потому что это BOOL (4 байта) в c ++

2) public bool bSetFocus должен быть Int32

Просто подумайте, что произойдет, если вы объявите самое большое поле последним?Возможно, P / Invoke копирует до последнего поля, которое заканчивается перед более ранними полями.

Попробуйте добавить поле размера, рассчитанное вручную, к атрибуту StructLayout, например:

[StructLayout(LayoutKind.Explicit, Size=...)]

Исходный код с bool содержал 13 байт, начиная с FieldOffset(4) ...
Тот самый MOUSE_EVENT_RECORD начиная с того же смещения, содержит 16 байт, начиная с того же смещения.

Когда вы изменили bool (1 байт) на uint (4 байта), вы составили 3 байта.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top