02 常见消息
1 打印消息相关信息
1.1 将消息内容转化为字符串
第一步: 定义字符串变量,用来保存转化后的消息
wchar_t szInfo[300]; //定义消息内容变量
第二步:用宽字符格式化函数转化消息内容\
wsprintf(szInfo, "hWnd=%d\tuMsg=%d\twParam=%d, lParam=%d",
hWnd, uMsg, wParam, lParam);
这里说明一下,为什么四个参数都可以直接以16进制形式进行格式化
参数 | 类型 | 本质 |
---|
hWnd | HWND | 结构体指针 | uMsg | UINT | unsigned int 无符号整型 | wParam | WPARAM | typedef UINT_PTR WPARAM; | lParam | LPARAM | typedef LONG_PTR LPARAM; |
不同的消息,wParam 与 lParam会有不同的意义
2 窗体创建消息
消息名: WM_CREATE
触发对象: 当调用CreateWindow 或 CreateWindowEx创建窗体完后触发
触发时机: 调用以上两个函数完毕后立即触发,此时窗体还未显示
附加参数意义:
wParam: 没用到
lParam: 一个指向 CREATESTRUCT 结构体的指针,类型为LPCREATESTRUCT
注意: 按照一般命名规则,类型名前加LP表示指针类型
CREATESTRUCT 结构体成员
typedef struct tagCREATESTRUCT {
LPVOID lpCreateParams; //指向将被用于创建窗口的数据的指针
HINSTANCE hInstance; //窗体所属应用程序句柄
HMENU hMenu; //窗体菜单句柄
HWND hwndParent; //窗体父窗体句柄
int cy; //窗体高度
int cx; //窗体宽度
int y; //窗体左上角y坐标
int x; //窗体左上角x坐标
LONG style; //窗体显示风格
LPCTSTR lpszName; //指向以结束符('\0')表示结尾的字符串,指定了新窗口的名字。
LPCTSTR lpszClass; //指向以结束符('\0')表示结尾的字符串,指定了新窗口的类名
DWORD dwExStyle; //窗体的扩展样式
} CREATESTRUCT, *LPCREATESTRUCT;
本例测试代码:
case WM_CREATE:
{
wchar_t szcsInfo[300];
LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lParam;
wsprintf(szcsInfo, L"窗体类名: %s, 窗体名称: %s\n",
lpcs->lpszClass, lpcs->lpszName);
OutputDebugString(szcsInfo);
break;
}
运行效果截图:
注意:
lParam是一个结构体指针,在使用时注意强制转化为相应的类型
LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lParam;
结构体类型: CREATESTRUCT
相应的结构体类型指针: LPCREATESTRUCT
在看帮助文档的时候,注意看清楚
3 窗口关闭消息
消息名: WM_CLOSE
触发对象: 点击窗口右上角的关闭(X)按钮
触发时机:点击后发送
wParam: 没用(0)
lParam: 没用(0)
示例代码:
case WM_CLOSE: //处理关闭按钮消息
{
int btnValue = MessageBox(hWnd, L"是否确认关闭窗口?", L"温馨提示", MB_YESNO);
if (btnValue == IDYES){
DestroyWindow(hWnd); //销毁窗口
break;
}
else{
return 1;
}
}
解析:
按下关闭按钮会弹出对话框,确认是否关闭
int btnValue = MessageBox(hWnd, L"是否确认关闭窗口?", L"温馨提示", MB_YESNO);
当弹出对话框按下确认按钮时,将会进入关闭窗口流程
if (btnValue == IDYES){
DestroyWindow(hWnd); //销毁窗口
break;
}
关闭主程序与关闭窗口是两个不同的事情
这个我们在上节课已经学过,关闭窗口只需要调用DestroyWindow即可
DestroyWindow(hWnd); //销毁窗口
但是关闭主程序还需要发送退出消息循环的命令
PostQuitMessage(0);
如果点击关闭然后点击取消按钮,即不想关闭,我们需要将关闭窗体的消息从消息对列清楚出去
else{
return 1;
}
当我们将消息交给系统处理程序的代码写在switch之外的时候,这个地方必须要return 一下,以防止关闭消息被系统处理程序重新处理一边,导致的窗体关闭
即:我们不主动处理窗口关闭事件,只要这个消息流入系统消息处理函数,系统也会帮我们关闭窗口.
但是如果关闭这个窗口后想要进一步关闭主程序,就必须在WM_DESTROY消息中发送退出消息循环的命令
PostQuitMessage(0);来结束程序的进程
如果系统消息处理函数是写在switch的default分支中的,则只需要直接break即可实现不关闭窗口的效果
测试代码如下:
LRESULT CALLBACK fWinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg){
case WM_CLOSE: //处理关闭按钮消息
{
int btnValue = MessageBox(hWnd, L"是否确认关闭窗口?", L"温馨提示", MB_YESNO);
if (btnValue == IDYES){
DestroyWindow(hWnd); //销毁窗口
}
break;
}
case WM_DESTROY:
PostQuitMessage(0); //发送退出消息
break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 1;
}
特别要注意:
(1) 在没有调用DestroyWindow函数前,窗体关闭过程可以中止,一旦调用了DestroyWindow,窗口关闭就无法逆转
因此关闭窗口的关键在于调用DestroyWindow函数的时机
(2) 再强调一下关闭窗口与关闭程序是两个不同的概念,关闭窗口只需要DestroyWindow即可,关闭程序要调用PostQuitMessage函数
4 鼠标消息
4.1 鼠标左键消息
消息名称 | 消息含义 |
---|
WM_LBUTTONDOWN | 鼠标左键按下(一个动作,不包含按下的状态) | WM_LBUTTONUP | 鼠标左键松开(弹起一瞬间的动作) | WM_LBUTTONDBLCLK | 鼠标左键双击动作(动作完成的一瞬间) |
组合键
wParam的可能取值:
值 | 含义 |
---|
MK_CONTROL | Ctrl键处于按下状态 | MK_LBUTTON | 左键处于按下状态 | MK_MBUTTON | 鼠标中键(滚轮键)处于按下状态 | MK_RBUTTON | 鼠标右键处于按下状态 | MK_SHIFT | shift键处于按下状态 | MK_XBUTTON1 | | MK_XBUTTON12 | |
判断方法是对可能的取值做与运算,结果为True则按下了,否则没有按下
如判断按下鼠标左键时是否已经按下了CTRL键
case WM_LBUTTONDOWN:
if (wParam & MK_CONTROL)
{
SetWindowText(hWnd,L"同时按下Ctrl+鼠标左键");
}
break;
注意:
用可能的取值去与wParam做&(按位与)运算来判断是否成立
又如: 判断ctrl + shift + 鼠标左键
case WM_LBUTTONDOWN:
if ((wParam & MK_CONTROL) && (wParam & MK_SHIFT)){
SetWindowText(hWnd,L"同时按下Ctrl + Shift + 鼠标左键");
}
break;
注意:
(1) WM_LBUTTONDWON 消息下MK_LBUTTON没有意义
(2) 在处理了WM_LBUTTONDOWN消息后直接处理WM_LBUTTONDBLCLK 时会导致双击消息无法获得响应
解决方法:
在WM_LBUTTONDOWN对本次的点击时间与上次的点击时间做差,如果差值在指定时间内,则为双击,不在指定时间为则为单击
获取时间的函数:GetMessageTime()函数来获取当次消息发送的时间
函数原型
LONG WINAPI GetMessageTime(void);
返回值是一个表示时间的长整型.这个时间与系统时间无关,从0开始,到超过取值范围后会从0重新开始
因此,如果要判断时间差,需要判断第二个时间>第一个时间
示例代码:
case WM_LBUTTONDOWN:
{
LONG clickTime = GetMessageTime();
if (lastClickTime == 0 || clickTime - lastClickTime > 500){
SetWindowText(hWnd, L"鼠标左键");
}
else{
SetWindowText(hWnd, L"双击鼠标左键");
}
lastClickTime = clickTime;
break;
}
注意: 为简化程序, 本例没有对时间做判断
鼠标的位置信息
lParam中包含了鼠标的位置信息,高位表示y坐标,低位表示x坐标
注意: 坐标是鼠标在窗体客户区的位置 ,不是以屏幕作为参考的坐标位置
有两种方法获取鼠标的x,y坐标
方法一:
int x = GET_X_LPARAM(lParam);
int y = GET_Y_LPARAM(lParam);
注意:此方法要求包含头文件:Windowsx.h
方法二:
int x = LOWORD(lParam);
int y = HIWORD(lParam);
示例代码:
case WM_LBUTTONDOWN:
{
//以下显示鼠标位置
int x2 = LOWORD(lParam);
int y2 = HIWORD(lParam);
int x1 = GET_X_LPARAM(lParam);
int y1 = GET_Y_LPARAM(lParam);
wchar_t mousePosInfo[100];
wsprintf(mousePosInfo, L"方法一: 鼠标的x坐标:%d, y坐标:%d\n 方法二: 鼠标的x坐标:%d, y坐标:%d\n",
x1, y1, x2, y2);
OutputDebugString(mousePosInfo);
break;
}
运行结果:
4.2 其他鼠标消息
消息名 | 消息含义 |
---|
WM_RBUTTONDOWN | 右键按下 | WM_RBUTTONUP | 右键松开(弹起) | WM_RBUTTONDBLCLK | 右键双击 | WM_MBUTTONDOWN | 中键(滚轮)按下 | WM_MBUTTONUP | 中键(滚轮)松开 | WM_MBUTTONDBLCLK | 中键(滚轮)双击 | WM_MOUSEMOVE | 鼠标移动 |
附加参数wParam与lParam的意义与用法同左键,不再展开
5 键盘事件
5.1 单个按键按下
消息名称: WM_KEYDOWN
wParam可能的取值, 常用按键表
值 | 含义 |
---|
VK_LBUTTON 0x01 | 鼠标左键按下状态 | VK_RBUTTON 0x02 | 鼠标右键按下状态 | VK_CANCEL 0x03 | Ctrl + Break | VK_MBUTTON 0x04 | 鼠标中键 | VK_XBUTTON1 0x05 | X1 mouse button | VK_XBUTTON2 0x06 | X2 mouse button | VK_BACK 0x08 | BackSpace | VK_TAB 0x09 | TAB键 | VK_CLEAR 0x0C | CLEAR key | VK_RETURN 0x0D | ENTER key 回车 | VK_SHIFT 0x10 | SHIFT key | VK_CONTROL 0x11 | CTRL key | VK_MENU 0x12 | ALT key | VK_PAUSE 0x13 | PAUSE key | VK_CAPITAL 0x14 | CAPS LOCK key | VK_KANA 0x15 | IME Kana mode 别问,问就是不知道 | VK_HANGUEL 0x15 | IME Hanguel mode (maintained for compatibility; use VK_HANGUL) 别问,问就是不知道 | VK_HANGUL 0x15 | IME Hangul mode 别问,问就是不知道 | VK_JUNJA 0x17 | IME Junja mode 别问,问就是不知道 | VK_FINAL 0x18 | IME final mode 别问,问就是不知道 | VK_HANJA 0x19 | IME Hanja mode 别问,问就是不知道 | VK_KANJI 0x19 | IME Kanji mode 别问,问就是不知道 | VK_ESCAPE 0x1B | ESC key | VK_CONVERT 0x1C | IME convert 别问,问就是不知道 | VK_NONCONVERT 0x1D | IME nonconvert 别问,问就是不知道 | VK_ACCEPT 0x1E | IME accept 别问,问就是不知道 | VK_MODECHANGE 0x1F | IME mode change request 别问,问就是不知道 | VK_SPACE 0x20 | SPACEBAR 空格键 | VK_PRIOR 0x21 | PAGE UP key | VK_NEXT 0x22 | PAGE DOWN key | VK_END 0x23 | END key | VK_HOME 0x24 | HOME key | VK_LEFT 0x25 | LEFT ARROW key 方向左 | VK_UP 0x26 | UP ARROW key 方向上 | VK_RIGHT 0x27 | RIGHT ARROW key 方向右 | VK_DOWN 0x28 | DOWN ARROW key 方向下 | VK_SELECT 0x29 | SELECT key | VK_PRINT 0x2A | PRINT key | VK_EXECUTE 0x2B | EXECUTE key | VK_SNAPSHOT 0x2C | PRINT SCREEN key | VK_INSERT 0x2D | INS key | VK_DELETE 0x2E | DEL key | VK_HELP 0x2F | HELP key | 0x30 | 0 key 大键数字0-9 | 0x31 | 1 key | 0x32 | 2 key | 0x33 | 3 key | 0x34 | 4 key | 0x35 | 5 key | 0x36 | 6 key | 0x37 | 7 key | 0x38 | 8 key | 0x39 | 9 key | 0x41 | A key 字母A-Z | 0x42 | B key | 0x43 | C key | 0x44 | D key | 0x45 | E key | 0x46 | F key | 0x47 | G key | 0x48 | H key | 0x49 | I key | 0x4A | J key | 0x4B | K key | 0x4C | L key | 0x4D | M key | 0x4E | N key | 0x4F | O key | 0x50 | P key | 0x51 | Q key | 0x52 | R key | 0x53 | S key | 0x54 | T key | 0x55 | U key | 0x56 | V key | 0x57 | W key | 0x58 | X key | 0x59 | Y key | 0x5A | Z key | VK_LWIN 0x5B | Left Windows key (Natural keyboard) 左win键 | VK_RWIN 0x5C | Right Windows key (Natural keyboard) 右win键 | VK_APPS 0x5D | Applications key (Natural keyboard) | VK_SLEEP 0x5F | Computer Sleep key | VK_NUMPAD0 0x60 | Numeric keypad 0 key 小键数字0-9 | VK_NUMPAD1 0x61 | Numeric keypad 1 key | VK_NUMPAD2 0x62 | Numeric keypad 2 key | VK_NUMPAD3 0x63 | Numeric keypad 3 key | VK_NUMPAD4 0x64 | Numeric keypad 4 key | VK_NUMPAD5 0x65 | Numeric keypad 5 key | VK_NUMPAD6 0x66 | Numeric keypad 6 key | VK_NUMPAD7 0x67 | Numeric keypad 7 key | VK_NUMPAD8 0x68 | Numeric keypad 8 key | VK_NUMPAD9 0x69 | Numeric keypad 9 key | VK_MULTIPLY 0x6A | Multiply key 小键盘乘号 | VK_ADD 0x6B | Add key 小键盘加号 | VK_SEPARATOR 0x6C | Separator key 小键盘除号 | VK_SUBTRACT 0x6D | Subtract key 小键盘减号 | VK_DECIMAL 0x6E | Decimal key 小键盘.键 | VK_F1 0x70 | F1 key 功能键F1-F24 (为什么我的键盘只到F12的) | VK_F2 0x71 | F2 key | VK_F3 0x72 | F3 key | VK_F4 0x73 | F4 key | VK_F5 0x74 | F5 key | VK_F6 0x75 | F6 key | VK_F7 0x76 | F7 key | VK_F8 0x77 | F8 key | VK_F9 0x78 | F9 key | VK_F10 0x79 | F10 key | VK_F11 0x7A | F11 key | VK_F12 0x7B | F12 key | VK_F13 0x7C | F13 key | VK_F14 0x7D | F14 key | VK_F15 0x7E | F15 key | VK_F16 0x7F | F16 key | VK_F17 0x80 | F17 key | VK_F18 0x81 | F18 key | VK_F19 0x82 | F19 key | VK_F20 0x83 | F20 key | VK_F21 0x84 | F21 key | VK_F22 0x85 | F22 key | VK_F23 0x86 | F23 key | VK_F24 0x87 | F24 key | VK_NUMLOCK 0x90 | NUM LOCK key NumLock键 | - 0x97-9F | Unassigned | VK_LSHIFT 0xA0 | Left SHIFT key 左Shift | VK_RSHIFT 0xA1 | Right SHIFT key 右Shfit | VK_LCONTROL 0xA2 | Left CONTROL key 左 Ctrl | VK_RCONTROL 0xA3 | Right CONTROL key 右Ctrl | VK_LMENU 0xA4 | Left MENU key 左Alt | VK_RMENU 0xA5 | Right MENU key 右Alt |
示例代码
case WM_KEYDOWN:
{
switch (wParam){
case 0x30:
SetWindowText(hWnd, L"0"); //按下了大键盘区的数字0键
break;
}
}
注意:
WM_KEYDOWN 消息不能捕获一些特殊的系统按键,如ALT
如果在实际应用中发现没有如期捕获到一些特殊的按键,可能是系统按键,需要用
WM_SYSKEYDOWN消息来捕获这些按键
该事件的附加参数wParam, lParam与WM_KEYDOWN消息的内容是一样的,就不再展开了
示例代码: 捕获ALT键
case WM_SYSKEYDOWN:
{
switch (wParam)
{
case VK_MENU:
{
OutputDebugString(L"ALT\n");
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
}
}
注意:
使用WM_SYSKEYDOWN 捕获系统按键后要交还给系统处理函数,否则有可能引起一些莫名其妙的错误(这里指我也不知道为什么会有什么后果的错误)
5.2 组合按键检测
当我们需要判断一个按键
用函数GetKeyState 与 GetAsyncKeyState来判断指定按键的状态
两者的区别在于键盘消息生成时的状态与当前调用函数时的按键状态
函数原型:
SHORT WINAPI GetKeyState(
_In_ int nVirtKey
);
参数: 按键的虚拟值,即上表中的内容
返回值: SHORT
返回值
高位为1时表示按键按下了,其他值为没有按下
低位为1时表示锁定状态,其他值为没有锁定(如CapsLock键,NumsLock键等)
示例代码: Ctrl+F1响应
case VK_F1: //F1键检测
{
SHORT bCtrl = GetKeyState(VK_CONTROL);
if (bCtrl >> 15)
{
OutputDebugString(L"CTRL+F1\n");
}
break;
}
示例代码: 大小写字母
case 0x41:
{
SHORT bCapsLock = GetKeyState(VK_CAPITAL);
if (bCapsLock & 1)
{
//大写锁定状态输出A
OutputDebugString(L"A");
}
else
{
//没有大写锁定状态输出a
OutputDebugString(L"a");
}
break;
}
Alt+的组合暂时搞不定_
6 字符消息
消息名称: WM_CHAR
消息发送是时机:
(1) 按下字母,数字键
(2) 调用了TranslateMessage函数
wParam: 字符
lParam: 同其他按键附加信息
示例代码: 输出按键字符
case WM_CHAR:
{
wchar_t charInfo[100];
wsprintf(charInfo, L"按下的字符:%c", (char)wParam);
SetWindowText(hWnd, charInfo);
}
如果不开启TranslateMessage 则无法生成WM_CHAR消息
7 绘图消息
消息名: WM_PAINT
消息发送时机: 当系统或其他应用程序请求绘制应用程序窗口的一部分时
wParam: 没用
lParam: 没用
示例代码:
case WM_PAINT:
{
//绘图刷子结构体
PAINTSTRUCT ps;
//绘图句柄
//第一个参数: 需要绘图的窗口句柄, 第二个参数: 绘图刷子结构体 返回值: 绘图句柄
HDC hDc = BeginPaint(hWnd, &ps); //开始绘图
Rectangle(hDc, 0,0, 200, 200); //画一个矩形, 后面四参数依次是:左上x,左上y,宽, 高
Ellipse(hDc,0,0,200,200);//椭圆(圆可以看成特殊的椭圆)
EndPaint(hWnd, &ps); //结束绘图
}
运行效果:
这个消息了解一下就好了.
8 其他有用的知识
8.1 在程序运行过程中观察变量动态
第一步:在需要监视变量的地方设置断点
第二步: 开启调试
第三步: 点击下方监视窗口
如果没有监视窗口,可以点菜单栏: 调试->窗口->监视窗口(Ctrl+Alt+W) 然后按1或2或3或4来选择监视窗口
第四步:将变量加入监视窗口
(1) 选中断点处的变量名
(2) 按住鼠标不要松开
(3)将变量名拖动到下方监视窗口
![img][10]
8.2 查看其他应用程序的窗口类名与窗口标题
第一步: 打开spy窗口
![img][11]
第二步: 打开偷窥窗口
![img][12]
![img][13]
第三步:点击要查看的应用程序窗口
![img][14]
效果图:
![img][15]
|