编写窗口过程
DispatchMessage 函数调用窗口的窗口过程,该窗口是消息的目标。 窗口过程具有以下签名。
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
通常有四个参数。
- hwnd 是窗口的句柄。
- uMsg 是消息代码;例如,WM_SIZE 消息指示窗口已调整大小。
- wParam 和 lParam 包含与消息相关的其他数据。 具体含义依赖于消息代码。
LRESULT 是程序返回到 Windows 的整数值。 它包含程序对特定消息的响应。 此值的含义取决于消息代码。 CALLBACK 是函数的调用约定。
典型的窗口过程只是一个打开消息代码的大型开关语句。 为要处理的每条消息添加事例。
switch (uMsg)
{
case WM_SIZE: // Handle window resizing
// etc
}
消息的其他数据包含在 lParam 和 wParam 参数中。 这两个参数都是整数值,表示指针宽度的大小(32 位或 64 位)。 每个参数的含义取决于消息代码 (uMsg)。 对于每条消息,都需要查找消息代码,并将参数强制转换为正确的数据类型。 通常,数据是数值或指向结构的指针。 某些消息没有任何数据。
例如,WM_SIZE 消息的文档指出:
- wParam 是一个标志,指示窗口是最小化、最大化还是调整大小。
- lParam 将窗口的新宽度和高度作为 16 位值纳入一个 32 位或 64 位数字中。 需要执行一些位移来获取这些值。 幸运的是,头文件 WinDef.h 包含执行此操作的帮助程序宏。
典型的窗口过程会处理数十条消息,因此可能变得相当长。 使代码更加模块化的一种方法是将用于处理每条消息的逻辑放在单独的函数中。 在窗口过程中,将 wParam 和 lParam 参数转换为正确的数据类型,并将这些值传递给函数。 例如,若要处理 WM_SIZE 消息,窗口过程将如下所示:
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_SIZE:
{
int width = LOWORD(lParam); // Macro to get the low-order word.
int height = HIWORD(lParam); // Macro to get the high-order word.
// Respond to the message:
OnSize(hwnd, (UINT)wParam, width, height);
}
break;
}
}
void OnSize(HWND hwnd, UINT flag, int width, int height)
{
// Handle resizing
}
LOWORD 和 HIWORD宏从 lParam 获取 16 位宽度和高度值。 窗口过程提取宽度和高度,然后将这些值传递给 OnSize
函数。
默认消息处理
如果不在窗口过程中处理特定消息,请将消息参数直接传递到 DefWindowProc 函数。 此函数对消息执行默认操作,该操作因消息类型而异。
return DefWindowProc(hwnd, uMsg, wParam, lParam);
避免窗口过程中的瓶颈
当窗口过程执行时,它会阻止在同一线程上创建的窗口的任何其他消息。 因此,请避免在窗口过程中进行冗长处理。 例如,假设程序打开 TCP 连接,并无限期等待服务器响应。 如果在窗口过程中执行此操作,则 UI 在请求完成之前不会响应。 在此期间,窗口无法处理鼠标或键盘输入、重新绘制自身,甚至无法关闭。
相反,应使用 Windows 中内置的其中一个多任务工具将工作移到另一个线程:
- 创建新线程。
- 使用线程池。
- 使用异步 I/O 调用。
- 使用异步过程调用。