1

以下は完全なプログラムです。上部の「#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) の理由は、ユニオンがオフセット 2 で始まるのを防ぐ 32 ビット構造のアラインメントを可能にするためです。

繰り返しますが、私の問題は PInvoke をまったく機能させていません。これらの追加の構造 (おそらく同じオフセットにある) がデータを単純に追加することで、データを混乱させている理由を理解しようとしています。

4

6 に答える 6

2

bSetFocus と dwCommandId を uint 型にすればうまくいくと思います。

今後は、適切な署名についてPInvoke wikiを確認してください。それは通常正確であるか、少なくとも良い出発点です。

于 2009-10-21T18:17:55.973 に答える
1

1) public bool bKeyDown は C++ の BOOL (4 バイト) であるため、Int32 bKeyDown である必要があります。

2) public bool bSetFocus は Int32 である必要があります

于 2009-10-21T18:21:17.720 に答える
1


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

}

于 2009-10-21T18:35:07.687 に答える
0

bool を使用した元のコードには、FieldOffset(4) で始まる 13 バイトが含まれていまし
MOUSE_EVENT_RECORD。同じオフセットで始まる 16 バイトが含まれていました。

bool (1byte) を uint(4bytes) に変更すると、3 バイトになりました。

于 2010-04-02T14:52:30.343 に答える
0

考えてみると、最大のフィールドを最後に宣言するとどうなるでしょうか? おそらく、P/Invoke は、前のフィールドの前に終了する最後のフィールドまでコピーしています。

于 2009-10-21T18:10:13.947 に答える
0

次のように、手動で計算された Size フィールドを StructLayout 属性に追加してみてください。

[StructLayout(LayoutKind.Explicit, Size=...)]
于 2009-10-21T18:20:36.373 に答える