Was mache ich falsch mit dieser Verwendung von StructLayout (LayoutKind.Explicit), wenn eine PInvoke Struktur mit Vereinigung nennen?

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

Frage

Im Folgenden ist ein komplettes Programm. Es funktioniert gut, solange man die "#define Defekt nicht tun Kommentar- an der Spitze. Der Bruch ist auf eine PInvoke eine Vereinigung richtig Marschall versagt. Die INPUT_RECORD Struktur in Frage hat eine Reihe von Substrukturen, die je nach dem Wert in Eventtype verwendet werden könnten.

Was ich nicht verstehe, das ist, wenn ich nur die einzelne Kind Struktur KEY_EVENT_RECORD definiere es mit der ausdrücklichen Erklärung arbeitet Offset 4. Aber wenn ich die anderen Strukturen in dem gleichen die Struktur der total abgespritzt Inhalt get Offsetzumischung.

//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;
    }
}

UPDATE:

Für diejenigen besorgt über die Struktur-Deklarationen selbst:

  1. bool als 32-Bit-Wert behandelt wird,
  2. Der Grund für den Offset (4) auf den Daten für die 32-Bit-Struktur Ausrichtung zu ermöglichen, die die Vereinigung von Anfang an verhindert, dass in 2 gegenüber.

Auch hier mein Problem macht durchaus nicht PInvoke Arbeit, es versucht, herauszufinden, warum diese zusätzlichen Strukturen (angeblich auf der gleichen Offset) werden die Daten fowling, indem sie einfach hinzufügen.

War es hilfreich?

Lösung


//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(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;
}

}

Andere Tipps

Ich glaube, es wird funktionieren, wenn Sie bSetFocus und dwCommandId vom Typ uint machen.

In Zukunft Besuche die PInvoke Wiki für die richtigen Signaturen. Es ist in der Regel genau oder zumindest ein guter Ausgangspunkt.

1) public bool bKeyDown sollte Int32 bKeyDown sein, weil es ein BOOL (4 Bytes) in c ++

ist

2) public bool bSetFocus sollte Int32

Nur ein Gedanke, was passiert, wenn erklären Sie das größte Feld an? Vielleicht P / Invoke ist das Kopieren bis zum letzten Feld, das vor früher Feldern endet.

Versuchen Sie, eine manuell berechnet Größe Feld zum StructLayout Attribut hinzufügen, wie folgt aus:

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

Der ursprüngliche Code mit dem Bool enthielt 13 Bytes, bei FieldOffset Start (4) ...
Die MOUSE_EVENT_RECORD am gleichen Offset beginnend conatianed 16 Bytes bei den gleichen Startversatz.

Wenn Sie die Bool geändert (1 Byte) zu einem uint (4 Bytes) aus, um die 3 Byte.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top