《Windows游戏编程大师技巧》学习笔记(七)
窗口事件
与窗口有关的消息简介:

WM_ACTIVATE消息
1 | bActive = LOWORD(wParam); // 激活标志 |
上述代码中bActive可能的取值如下:

bMinimized表示窗口是否已最小化;hwndPrevious指将被激活或被取消激活的句柄,具体含义由bActive的值决定:如果bActive的值为WA_ACTIVE或WA_CLICKACTIVE,hwndPrevious是被取消激活的窗口的句柄,该句柄可能为NULL;
1 | case WM_ACTIVATE: |
WM_CLOSE消息
WM_CLOSE会在WM_DESTROY和WM_QUIT之前被发送,也就是说此消息说明用户正在试图关闭窗口;
一个常见的窗口关闭处理逻辑如下,当用户尝试关闭窗口时弹出消息框询问是否关闭窗口:

1 | case WM_CLOSE: |
WM_SIZE消息
1 | fwSizeType = wParam; // 窗口尺寸改变标志 |
fwSizeType表示刚发生的尺寸变动是那种改变,可能的值如下:

WM_MOVE消息
1 | xPos = (int)LOWORD(lParam); // 移动后的窗口横坐标 |
注意,WM_MOVE和WM_SIZE消息类似,只有当窗口移动结束(或尺寸改变结束)时才会被发送,如果想要实时跟踪窗口的移动(或尺寸改变),那么就需要用对应的-ING事件;
键盘事件
当用户按下键盘某个键时,会产生两个数据:扫描码和ASCII码;
Windows下处理键盘消息有三种途径:
- 通过
WM_CHAR消息,传递ASCII码,ASCII码是人为形成的数据,是用户通过按键(组合)具体输入的字符; - 通过
WM_KEYDOWN和WM_KEYUP消息,传递扫描码,扫描码是唯一指定给键盘上每一个键的编码,与是否按下Shift键等无关; - 通过调用
GetAsyncKeyState函数,该函数可以查询某键的最后状态;
WM_CHAR消息
1 | int ascii_code = wparam; // 所按键的ASCII码 |
key_state描述可能被按下的特殊按键,其按位编码如下表:

1 |
|
WM_KEY*消息
WM_KEYDOWN和WM_KEYUP这类消息,传递的数据是未经处理的,为该键的虚拟扫描码;
虚拟扫描码由Windows翻译键盘产生的标准扫描码翻译得到,屏蔽了不同厂商不同型号键盘标准扫描码不同的场景,方便开发者编程;
1 | int virtual_code = (int)wparam; |
virtual_code为所按键的虚拟键代码,key_state与WM_CHAR消息中的一样,描述可能被按下的特殊控制键;
虚拟键编码如下表所示:

GetAsyncKeyState函数
使用GetKeyboardState、GetKeyState或GetAsyncKeyState函数都可以查询键盘状态,此处重点讨论GetAsyncKeyState,函数原型如下:
1 | SHORT WINAPI GetAsyncKeyState(int vKey); |
只需要传递给函数想要检测的虚拟键代码,返回值的最高位便表示该键是否被按下,如下代码检测回车键是否被按下:
1 | if (GetAsyncKeyState(VK_RETURN) & 0x8000) |
鼠标事件
鼠标坐标总览如下图所示:

WM_MOUSEMOVE消息
1 | int mouse_x = (int)LOWORD(lParam); |
mouse_x和mouse_y意义显而易见,表示鼠标在窗口坐标系下的横纵坐标;buttons记录按键编码,可以通过与运算检测哪些键被按下,按键编码如下:

1 | case WM_MOUSEMOVE: |
WM_*BUTTON*消息
由于WM_MOUSEMOVE消息只会在鼠标移动时才会发送,所以当我们想要独立处理鼠标按键事件时,就需要将逻辑放置到如下的按键消息下:

当按键消息触发时,鼠标当前的坐标位置信息也被存储到了lParam中,如下的代码可以在鼠标左键被双击时获取当前的鼠标坐标:
1 | case WM_LBUTTONDBLCLK: |
用户自定义事件
有两种方式主动向窗口发送消息:
SendMessage函数:优先度较高,窗口接收该消息后立即调用WinProc函数,函数返回值为对应WinProc函数的返回值,在非Unicode环境下函数原型如下:1
2
3
4
5
6LRESULT WINAPI SendMessageA(
HWND hWnd, // 窗口句柄
UINT Msg, // 消息类型
WPARAM wParam, // 第一个消息参数
LPARAM lParam // 第二个消息参数
);PostMessage函数:优先度较低,只是将消息发往窗口的消息队列,而后直接返回,如果返回非零值则表示执行成功,在非Unicode环境下函数原型如下:1
2
3
4
5
6BOOL WINAPI PostMessageA(
HWND hWnd, // 窗口句柄
UINT Msg, // 消息类型
WPARAM wParam, // 第一个消息参数
LPARAM lParam // 第二个消息参数
);
如下代码可以让程序在Esc键被按下时退出:
1 | if (GetAsyncKeyState(VK_ESCAPE) & 0x8000) |
需要注意的是,由于SendMessage函数会直接调用WinProc函数对传入的事件进行处理,所以可能跳过当前已存在于事件队列中的事件,导致执行顺序被打乱,相对来说使用PostMessage函数更为安全;
发送自定义消息时可以使用WM_USER作为消息类型,可以根据需要任意设置wparam和lparam的值,如下代码可以为自定义的内存管理系统创建消息:
1 |
|
《Windows游戏编程大师技巧》学习笔记(七)



