Windows基础知识(2)——鼠标键盘消息处理

前置芝士

焦点

一般来说,我们选中一个窗口就叫获取了一个焦点,键盘输入事件只会发送给具有焦点的窗口或者是控件。我们按下一个按键,会将我们的按键格式化成一个消息给内核,内核通过打包这个消息发送给具有焦点的窗体应用程序。

键盘事件

我们可以处理的键盘按键大致分为两类:

  • 系统按键:WM_KEYDOWN,WM_KEYUP。
  • 非系统按键:WM_SYSKEYDOWN,WM_SYSKEYUP。

WM_KEYDOWN 事件表示一个键按下,如果一直按住会持续地发送此类消息。

1
2
3
4
5
6
7
8
9
case WM_KEYDOWN: {
switch (wParam) {
case 'B': {
MessageBox(NULL, L"123", L"123", MB_OK);
break;
}
}
break;
}

WM_KEYUP 就是键盘弹起事件。

判断有没有按住某个按键使用 GetKeyBoardState(nVirtKey),参数是虚拟键码,如果按住这个键则返回 <0 的值。

下面是常见键盘键码的说明:

返回的常量 Value 说明
VK_LBUTTON 0x01 鼠标左键
VK_RBUTTON 0x02 鼠标右键
VK_CANCEL 0x03 控制中断处理
VK_MBUTTON 0x04 中间鼠标按钮 (三键鼠标)
VK_XBUTTON1 0x05 X1 鼠标按钮
VK_XBUTTON2 0x06 X2 鼠标按钮
- 0x07 Undefined
VK_BACK 0x08 BACKSPACE 密钥
VK_TAB 0x09 Tab 键
- 0x0A-0B 预留
VK_CLEAR 0x0C CLEAR 键
VK_RETURN 0x0D Enter 键
- 0x0E-0F Undefined
VK_SHIFT 0x10 SHIFT 键
VK_CONTROL 0x11 Ctrl 键
VK_MENU 0x12 Alt 键
VK_PAUSE 0x13 PAUSE 键
VK_CAPITAL 0x14 CAPS LOCK 键
VK_KANA 0x15 IME Kana 模式
VK_HANGUEL 0x15 IME 朝鲜文库埃尔模式 (保持兼容性;使用 VK_HANGUL)
VK_HANGUL 0x15 IME Hanguel 模式
VK_IME_ON 0x16 IME On
VK_JUNJA 0x17 IME Junja 模式
VK_FINAL 0x18 IME 最终模式
VK_HANJA 0x19 IME Hanja 模式
VK_KANJI 0x19 IME Kanji 模式
VK_IME_OFF 0x1A IME 关闭
VK_ESCAPE 0x1B ESC 键
VK_CONVERT 0x1C IME 转换
VK_NONCONVERT 0x1D IME 不转换
VK_ACCEPT 0x1E IME 接受
VK_MODECHANGE 0x1F IME 模式更改请求
VK_SPACE 0x20 空格键
VK_PRIOR 0x21 PAGE UP 键
VK_NEXT 0x22 PAGE DOWN 键
VK_END 0x23 END 键
VK_HOME 0x24 HOME 键
VK_LEFT 0x25 向左键
VK_UP 0x26 向上键
VK_RIGHT 0x27 向右键
VK_DOWN 0x28 向下键
VK_SELECT 0x29 SELECT 键
VK_PRINT 0x2A PRINT 键
VK_EXECUTE 0x2B EXECUTE 键
VK_SNAPSHOT 0x2C 打印屏幕键
VK_INSERT 0x2D INS 密钥
VK_DELETE 0x2E DEL 键
VK_HELP 0x2F 帮助密钥
- 0x3A-40 Undefined
0x41 密钥
VK_LWIN 0x5B 左Windows键 (自然键盘)
VK_RWIN 0x5C 右Windows键 (自然键盘)
VK_APPS 0x5D 应用程序键 (自然键盘)
- 0x5E 保留
VK_SLEEP 0x5F 计算机休眠键
VK_NUMPAD0 0x60 数字键盘 0 键
VK_NUMPAD1 0x61 数字键盘 1 键
VK_NUMPAD2 0x62 数字键盘 2 键
VK_NUMPAD3 0x63 数字键盘 3 键
VK_NUMPAD4 0x64 数字键盘 4 键
VK_NUMPAD5 0x65 数字键盘 5 键
VK_NUMPAD6 0x66 数字键盘 6 键
VK_NUMPAD7 0x67 数字键盘 7 键
VK_NUMPAD8 0x68 数字键盘 8 键
VK_NUMPAD9 0x69 数字键盘 9 键
VK_MULTIPLY 0x6A 乘键
VK_ADD 0x6B 添加密钥
VK_SEPARATOR 0x6C 分隔符键
VK_SUBTRACT 0x6D 减去键
VK_DECIMAL 0x6E 十进制键
VK_DIVIDE 0x6F 除键
VK_F1 0x70 F1 键
VK_F2 0x71 F2 键
VK_F3 0x72 F3 键
VK_F4 0x73 F4 键
VK_F5 0x74 F5 键
VK_F6 0x75 F6 键
VK_F7 0x76 F7 键
VK_F8 0x77 F8 键
VK_F9 0x78 F9 键
VK_F10 0x79 F10 键
VK_F11 0x7A F11 键
VK_F12 0x7B F12 键
VK_F13 0x7C F13 键
VK_F14 0x7D F14 键
VK_F15 0x7E F15 键
VK_F16 0x7F F16 键
VK_F17 0x80 F17 键
VK_F18 0x81 F18 键
VK_F19 0x82 F19 键
VK_F20 0x83 F20 键
VK_F21 0x84 F21 键
VK_F22 0x85 F22 键
VK_F23 0x86 F23 键
VK_F24 0x87 F24 键
- 0x88-8F 未分配
VK_NUMLOCK 0x90 NUM LOCK 密钥
VK_SCROLL 0x91 SCROLL LOCK 键
0x92-96 OEM 特定
- 0x97-9F 未分配
VK_LSHIFT 0xA0 左 SHIFT 键
VK_RSHIFT 0xA1 右 SHIFT 键
VK_LCONTROL 0xA2 左 Ctrl 键
VK_RCONTROL 0xA3 右 Ctrl 键
VK_LMENU 0xA4 左 Alt 键
VK_RMENU 0xA5 右 ALT 键
VK_BROWSER_BACK 0xA6 浏览器后退键
VK_BROWSER_FORWARD 0xA7 浏览器前进键
VK_BROWSER_REFRESH 0xA8 浏览器刷新键
VK_BROWSER_STOP 0xA9 浏览器停止键
VK_BROWSER_SEARCH 0xAA 浏览器搜索键
VK_BROWSER_FAVORITES 0xAB 浏览器收藏键
VK_BROWSER_HOME 0xAC 浏览器“开始”和“主页”键
VK_VOLUME_MUTE 0xAD 静音键
VK_VOLUME_DOWN 0xAE 音量减小键
VK_VOLUME_UP 0xAF 音量增加键
VK_MEDIA_NEXT_TRACK 0xB0 下一曲目键
VK_MEDIA_PREV_TRACK 0xB1 上一曲目键
VK_MEDIA_STOP 0xB2 停止媒体键
VK_MEDIA_PLAY_PAUSE 0xB3 播放/暂停媒体键
VK_LAUNCH_MAIL 0xB4 启动邮件键
VK_LAUNCH_MEDIA_SELECT 0xB5 选择媒体键
VK_LAUNCH_APP1 0xB6 启动应用程序 1 键
VK_LAUNCH_APP2 0xB7 启动应用程序 2 键
- 0xB8-B9 预留
VK_OEM_1 0xBA 用于其他字符;它可能因键盘而异。 对于美国标准键盘,“;:”键
VK_OEM_PLUS 0xBB 对于任何国家/地区,“+”键
VK_OEM_COMMA 0xBC 对于任何国家/地区,“,键
VK_OEM_MINUS 0xBD 对于任何国家/地区,“-”键
VK_OEM_PERIOD 0xBE 对于任何国家/地区,“.”键
VK_OEM_2 0xBF 用于其他字符;它可能因键盘而异。 对于美国标准键盘,“/?” key
VK_OEM_3 0xC0 用于其他字符;它可能因键盘而异。 对于美国标准键盘,“~”键
- 0xC1-D7 预留
- 0xD8-DA 未分配
VK_OEM_4 0xDB 用于其他字符;它可能因键盘而异。 对于美国标准键盘,“[{”键
VK_OEM_5 0xDC 用于其他字符;它可能因键盘而异。 对于美国标准键盘,“|”键
VK_OEM_6 0xDD 用于其他字符;它可能因键盘而异。 对于美国标准键盘,“]}”键
VK_OEM_7 0xDE 用于其他字符;它可能因键盘而异。 对于美国标准键盘,“单引号/双引号”键
VK_OEM_8 0xDF 用于其他字符;它可能因键盘而异。
- 0xE0 保留
0xE1 OEM 特定
VK_OEM_102 0xE2 <>美国标准键盘上的键,或`\
0xE3-E4 OEM 特定
VK_PROCESSKEY 0xE5 IME PROCESS 密钥
0xE6 OEM 特定
VK_PACKET 0xE7 用于将 Unicode 字符当作键击传递。 该 VK_PACKET 键是用于非键盘输入方法的 32 位虚拟键值的低字。 有关详细信息,请参阅“备注”,以及KEYBDINPUTSendInputWM_KEYDOWNWM_KEYUP
- 0xE8 未分配
0xE9-F5 OEM 特定
VK_ATTN 0xF6 Attn 键
VK_CRSEL 0xF7 CrSel 键
VK_EXSEL 0xF8 ExSel 密钥
VK_EREOF 0xF9 擦除 EOF 密钥
VK_PLAY 0xFA 播放键
VK_ZOOM 0xFB 缩放键
VK_NONAME 0xFC 预留
VK_PA1 0xFD PA1 键
VK_OEM_CLEAR 0xFE 清除键

在上面,大写字母和数字就是它对应的 ASCII 值。

这里再学习几个 API:

SendMessage(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 用于向指定窗口发送一个消息,它的参数和主过程调用函数一致,通过这个我们可以自定义消息类型。

寻找窗口句柄,可以根据窗口类,也可以根据窗口标题,FindWindowA([in, optional] LPCSTR lpClassName,[in, optional] LPCSTR lpWindowName); 函数,当我们不需要使用窗口类寻找时,就把窗口类参数置为 NULL 即可。

鼠标事件

三种状态,三个按钮。

所谓三个按钮就是:

  • 左键(LBUTTON)
  • 中键(RBUTTON)
  • 右键(MBUTTON)

所谓三个状态就是:

  • 单击(DOWN,UP)
  • 双击(DBLCLK)
  • 拖动

一般来说,WM_ 开头的宏都是顶层 Message 事件。

这三个按键,三个状态可以组成九种不同的消息,在这九个消息中,鼠标的位置被记录在了 lparam 参数中,低 16 位表示了 x 轴的坐标,高 16 位表示了 y 轴的坐标。

wParam 记录了同时间有没有其它按键,使用 MK_LBUTTON & wParam 可以判断是否按下了左键。

据此,我们可以写一个直线绘制的项目

1
2
3
4
5
6
7
8
9
10
11
12
13
case WM_LBUTTONDOWN: {
y = HIWORD(lParam);
x = LOWORD(lParam);
}
break;
case WM_LBUTTONUP:{
HPEN hPen = CreatePen(BS_SOLID, 1, RGB(255,0,0));
HDC hDC = GetDC(hWnd);
SelectObject(hDC, hPen);
MoveToEx(hDC, x, y, NULL);
LineTo(hDC, LOWORD(lParam), HIWORD(lParam));
ReleaseDC(hWnd,hDC);
}

主要原理就是在按下左键的时候记录坐标,弹起时创建画笔并绘制。