一 引言
本文主要介绍几种Windows常见的消息的处理,在《Win32 SDK基础(8)—— Windows消息机制》中,我们介绍了WM_CREATE消息的处理,在窗口创建之前,我们利用消息处理函数弹出了一个MessageBox,本文在此基础之上,介绍WM_DESTROY、WM_SYSCOMMAND、WM_QUIT、WM_SIZE等其它常见的Windows消息。首先,我们引入在《Win32 SDK基础(8)—— Windows消息机制》文中的代码,后续的实验都在此代码的基础之上。
#include "stdafx.h"
#include "MessageTs.h"
HINSTANCE g_hInstance = 0;
//窗口处理函数
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);//可以使GetMessage返回0
break;
case WM_CREATE:
MessageBox(NULL,"WM_CREATE消息被处理了","消息处理",MB_OK);
default:
break;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
//注册窗口类
BOOL Register(LPSTR lpClassName, WNDPROC wndProc)
{
WNDCLASSEX wce = { 0 };
wce.cbSize = sizeof(wce);
wce.cbClsExtra = 0;
wce.cbWndExtra = 0;
wce.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wce.hCursor = NULL;
wce.hIcon = NULL;
wce.hIconSm = NULL;
wce.hInstance = g_hInstance;
wce.lpfnWndProc = wndProc;
wce.lpszClassName = lpClassName;
wce.lpszMenuName = NULL;
wce.style = CS_HREDRAW | CS_VREDRAW;
ATOM nAtom = RegisterClassEx(&wce);
if (nAtom == 0)
return FALSE;
return true;
}
//创建主窗口
HWND CreateMain(LPSTR lpClassName, LPSTR lpWndName)
{
HWND hWnd = CreateWindowEx(0, lpClassName, lpWndName,
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, g_hInstance, NULL);
return hWnd;
}
//显示窗口
void Display(HWND hWnd)
{
ShowWindow(hWnd, SW_SHOW);
UpdateWindow(hWnd);
}
//消息循环
void Message()
{
MSG nMsg = { 0 };
while (GetMessage(&nMsg, NULL, 0, 0))
{
TranslateMessage(&nMsg);
DispatchMessage(&nMsg);
}
}
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
// TODO: Place code here.
g_hInstance = hInstance;
BOOL nRet = Register("Main", WndProc);
if (!nRet)
{
MessageBox(NULL, "注册失败", "Infor", MB_OK);
return 0;
}
HWND hWnd = CreateMain("Main", "window");
Display(hWnd);
Message();
return 0;
}
二、WM_CREATE消息
我们还是要再介绍下WM_CREATE消息。因为在上一文中我们只通过弹出MessageBox验证了WM_CREATE消息的产生时机,却没有介绍它另外一个很重要的构成——WPARAM和LPARAM参数。我们在发送消息时,往往可以通过这两个参数携带一些信息。WM_CREATE消息是我们在创建窗口时由系统自动发送的消息,同样也会利用这两个参数携带信息。LPARAM携带了我们创建窗口的CreateWindowEx的12个参数信息,WPARAM没有被使用,下面我们在处理WM_CREATE消息时,在弹出的对话框上显示窗口类和窗口名称。
//窗口处理函数
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);//可以使GetMessage返回0
break;
case WM_CREATE:
{
CREATESTRUCT crt = *((CREATESTRUCT*)lParam);
char buf[256] = {0};
sprintf(buf,"创建的窗口类名称是%s,窗口名称是%s",crt.lpszClass,crt.lpszName);
MessageBox(NULL, buf, "消息处理", MB_OK);
}
default:
break;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
运行程序,我们发现在窗口创建之前我们成功捕获了窗口的注册窗口类名称和窗口名称,并在对话框上显示出来:
三、WM_DESTROY消息
这个消息在之前的文章中我们其实早已经解除过。他是Windows窗口关闭时发送的一个消息,简单点说就是,当你点击Windows窗口的关闭按钮时,会发出这个消息。我们在之前的代码中,处理这个消息的操作是PostQuitMessage(0),这其实是我们发送了一个接下来要介绍的WM_QUIT消息用来结束程序的进程。WM_DESTORY消息的LPARAM和WPARAM都没被使用,它一般被用来做一些窗口关闭前资源的回收和内存的释放工作等等,我们调用的PostQuitMessage(0)就是一个很常见的用法。
四、WM_QUIT消息
前面我们已经介绍过,这是我们使用PostQuitMessage(0)发送的一个消息,用来结束程序进程,它的WMPARAM是PostQuitMessage中传递的参数,LPARAM参数未被使用。通常情况下我们发送了WM_QUIT后,会引起消息循环中的GetMessage函数返回,从而使得进程退出。WM_QUIT消息是无法传递到我们自己的窗口处理函数中的。但是我们可以从侧面验证有这么一个消息:我们先在代码中增加对WM_QUIT消息的处理。
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);//可以使GetMessage返回0
break;
case WM_CREATE:
{
CREATESTRUCT crt = *((CREATESTRUCT*)lParam);
char buf[256] = {0};
sprintf(buf,"创建的窗口类名称是%s,窗口名称是%s",crt.lpszClass,crt.lpszName);
MessageBox(NULL, buf, "消息处理", MB_OK);
}
case WM_QUIT:
{
int param = (int)wParam;
char buf[256];
sprintf(buf, "进程退出,退出码:%d", param);
MessageBox(NULL, buf, "消息处理", MB_OK);
}
default:
break;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
运行程序,在关闭处理WM_CREATE消息的对话框后,发现弹出了我们验证WM_QUIT消息的对话框。
这是因为,弹出的处理WM_CREATE消息MessageBox也是一个窗口,但它属于我们的进程。每个窗口都有一个消息循环,但是消息队列一个进程却只有一个,当MessageBox的窗口关闭时,它的窗口处理函数也调用PostQuitMessage(0)发送了一个WM_QUIT消息。它的消息循环中GetMessage接收到WM_QUIT消息后返回并退出。而这个消息又会被我们的消息循环捕获到,但是由于它不是我们的窗口发出的消息,所以不会导致我们的GetMessage返回结束循环,反而被传给我们的窗口处理函数汇总进行处理,所以出现了上面截图显示的对话框。
五、WM_SYSCOMMAND消息
关闭:
SC_CLOSE
最大化:
SC_MAXIMIZE
最小化:
SC_MINIMIZE
这里只列举了三种常见的WM_SYSCOMMAND携带的参数宏,其它的可以参照MSDN。WM_SYSCOMMAND的lParam携带的是产生该消息的鼠标的位置,位置的X和Y坐标分别被存放在lParam的低位和高位字中,我们用下面的代码来验证在窗口最大化时,我们鼠标的位置:
case WM_SYSCOMMAND:
{
if (wParam == SC_MAXIMIZE)
{
short x = LOWORD(lParam);
short y = HIWORD(lParam);
char buf[256];
sprintf(buf, "窗口最大化,x坐标:%d,y坐标:%d", x,y);
MessageBox(NULL, buf, "消息处理", MB_OK);
}
}
当我们点击窗口的最大化按钮时,出现下面的提示:
六 WM_SIZE消息
每当我们调整窗口的大小时,都会触发WM_SIZE消息。它的wParam参数携带的是该消息产生的原因:
SIZE_RESTORED —— 重新调整窗口大小
SIZE_MAXIMIZED —— 最大化显示
SIZE_ MINIMIZED —— 最小化显示
其他参数宏详见MSDN
它的lParam参数携带的是重新调整大小后的窗口的高和宽,其中低字节代表宽,高字节代表高,这里我们通过代码验证,当窗口最大化后窗口的高和宽:
case WM_SIZE:
{
if (wParam == SIZE_MAXIMIZED)
{
short width = LOWORD(lParam);
short hight = HIWORD(lParam);
char buf[256];
sprintf(buf, "窗口最大化,高度:%d,宽度:%d", hight, width);
MessageBox(NULL, buf, "消息处理", MB_OK);
}
}
运行程序,最大化时可以显示最大化后的窗口高度和宽度:
Github
位置:
https://github.com/HymanLiuTS/Win32SDK
克隆本项目:
git clone git@github.com:HymanLiuTS/Win32SDK.git
获取本文源代码:
git checkout WL10
https://github.com/HymanLiuTS/Win32SDK
克隆本项目:
git clone git@github.com:HymanLiuTS/Win32SDK.git
获取本文源代码:
git checkout WL10