还记得上篇中最后说到一个问题吗?当我们关闭程序窗口时,窗口确实是关闭了,可是程序并没有退出啊。为什么呢???
一. 理解程序的退出条件:
首先,我们要先明白程序退出的条件,看上篇中的这段代码:
while(GetMessage(&msg, NULL, 0, 0)) //获取一个消息,成功后会放在msg中。
{
TranslateMessage(&msg); //消息进行必要的预处理转换。
DispatchMessage(&msg); //调用WinProc回调函数,将msg传递给WinProc函数
}
如果程序一直在这个消息循环中,程序就没能退出。只有当GetMessage收到一个WM_QUIT的消息,则返回值才会为零,退出循环,程序得以结束。(这个道理应该好理解吧?)
二. 点关闭按钮时,发生了什么
当我们点窗口右上角的关闭按钮时,到底发生了什么事呢?(请边看源代码,边体会下面的分析噢!)
第一. 它并没有(或最终没有导致)发出WM_QUIT的消息。因此GetMessage函数不会收到WM_QUIT消息,就没法跳出循环了。(那么又产生了什么消息呢?)
第二. 点关闭按钮时,产生WM_CLOSE的消息。GetMessage会收到WM_CLOSE消息的MSG结构信息。
第三. 按前篇所述的消息处理流程可知:DespatchMessage会调用WinProc回调函数,并把WM_CLOSE消息的相关信息传递给WinProc函数参数中。
第四. 现在我们的WinProc里只有一句:
LRESULT CALLBACK WinProc(HWND hwnd,
UINT msg,
WPARAM wparam,
LPARAM lparam)
{
//这里可以添加你的消息处理代码
return DefWindowProc(hwnd, msg, wparam, lparam);
}
它将WM_CLOSE继续传递给缺省窗口过程函数DefWindowProc。
第五. 在DefWindowProc函数里,判断是WM_CLOSE消息后,就会对参数hwnd所代表的窗口进行销毁。(看吧,销毁窗口的事也是由DefWindowProc来完成了。)
第六. 成功销毁窗口后,DefWindowProc里接着还会发一个WM_DESTROY的消息到消息队列中(表示说窗口已经被销毁了)。然后DefWindowProc函数才结束。
第七. 回到我们的消息循环的GetMessage函数。这个函数又会获得WM_DESTROY消息的信息,开始了下一个消息处理过程。
第八. 这个WM_DESTROY可在WinProc函数中由我们处理。但在WinProc函数体的代码中我们没有自己去处理它。仍然是让DefWindowProc去处理。
第九. 然而,DefWindowProc只是简单地把它给“扔掉”了。
第十. 整个点窗口右上角的关闭按钮作所的所有动作就这样完成了。
你看,上述中,上述程序始终没有产生WM_QUIT的消息,所以窗口确实是销毁了,但程序并没有退出这个消息处理循环。
哦,怪不得我们的程序没法结束了。(那该怎么办呢?)
三. 如何使程序结束。
退出程序的三点说明:
1. 我们希望是通过单击这个窗口右上角的关闭按钮时来退出程序。
2. 应该在窗口成功销毁后,才让程序退出。
3. 只要让程序产生一个WM_QUIT消息,就可以退出循环而结束程序。
终上所述,程序应在收到WM_DESTROY消息后才能退出程序。因为WM_DESTROY消息表示窗口已经销毁。
那么我们又如何才能产生一个WM_QUIT的消息呢?用下面这个--API函数:
PostQuitMessage(0);
参数代入0值就可。它将产生一个WM_QUIT消息。WM_QUIT消息最终会被GetMessage函数“摘取”到并返回0值。从而退出循环,结束程序。看我实现代码:
LRESULT CALLBACK WinProc(HWND hwnd,
UINT msg,
WPARAM wparam,
LPARAM lparam)
{
switch (msg) //msg中保存的就是正要处理的消息
{
case WM_DESTROY: //这是我们自行处理的第一个消息
{
PostQuitMessage(0); //发出一个WM_QUIT消息
return 0; //然后直接返回。
}break;
default:break;
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
int WINAPI WinMain(HINSTANCE hinstance,
HINSTANCE hprevinstance,
LPSTR lpcmdline,
int ncmdshow)
{
HWND hWnd;
MSG msg;
WNDCLASSEX wndclass;
//…… 这里省略了前面所述的注册窗口类的过程
//
hWnd=CreateWindowEx(NULL,WND_CLS_NAME,
"这是我的第一个窗口",
WS_OVERLAPPEDWINDOW|WS_VISIBLE ,
CW_USEDEFAULT, 0,
400,400,
NULL,
NULL,
hinstance,
NULL );
if (!hWnd)
return 0;
ShowWindow(hWnd, ncmdshow);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
WinProc函数参数中的UINT msg就是程序传递进来的消息标识。我们只要判断msg是否为WM_DESTROY消息,如果是就发一个WM_QUIT消息。
补充说明一点:
WinProc函数参数中有一个msg变量,而WinMain函数中也定义了一个msg。不要把它们给混了啊!它们可是不同的变量啊!WinMain中定义的msg类型是MSG,在Winuser.h中已定义如下:
typedef struct tagMSG {
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
} MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;
你看,它是一个结构体。而WinProc参数中的msg是一个UINT类型,它其实只是WinProc函数里的msg结构成员message的值。
你看看DispatchMessage(&msg);是如何传递这个msg给WinProc的:
它在调用WinProc时,会——
将msg.hwnd值传递给WinProc参数中的HWND hwnd;
将msg.message值传递给WinProc参数中的UINT msg;
将msg.wParam传递给WinProc参数中的WPARAM wParam;
将msg.lParam值传递给WinProc参数中的LPARAM lParam。
整个程序实现退出的流程还是让你自行去分析啦!应该不会太难吧!?
至此,终于完成了这个SDK程序的基本框架了!(哈,真值得我们去举杯庆祝啦!不过,我的任务还没完啊!还有许多要解决的问题等着我噢!)。
评论