《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游戏编程大师技巧》学习笔记(七)