下面是一个完整的程序。只要您不取消注释顶部的“#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;
}
}
更新:
对于那些担心结构声明本身的人:
- bool 被视为 32 位值
- 数据上使用 offset(4) 的原因是允许 32 位结构对齐,这可以防止联合从偏移量 2 开始。
同样,我的问题根本不是使 PInvoke 工作,它试图弄清楚为什么这些额外的结构(假设在相同的偏移量)通过简单地添加它们来收集数据。