2.2.7 Windows程序关闭窗口的实现过程
liebian365 2024-10-26 12:59 4 浏览 0 评论
2.2.7 第14练:处理WM_DESTROY消息
/*------------------------------------
014 编程达人win32 API每日一练
第14个例子WM_DESTROY.C:回调函数---处理WM_DESTROY消息
WM_DESTROY消息
PostQuitMessage函数
注意:关闭窗口后,程序退出。
(c) www.bcdaren.com 编程达人
-----------------------------------------*/
#include <windows.h>
/***************回调函数******************/
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //消息回调函数
/************程序入口 *********************/
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
……(略)
return msg.wParam;//msg.wParam 来自一条表示退出的消息,返回这个值给系统
}
/**************回调函数——消息处理过程*********/
//窗口回调函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hDC; //设备环境——绘图的地方
PAINTSTRUCT ps; //绘图结构体变量
RECT rect; //绘图区范围
switch (message)
{
case WM_CREATE: //创建窗口消息
//return 0;
break; //也可以
case WM_PAINT://绘图函数,在窗口上绘图!
hDC = BeginPaint(hwnd, &ps);
//获得客户区大小
GetClientRect(hwnd, &rect);
//绘制字符串
TextOut(hDC, 200, 200, TEXT("爱达人!"), lstrlen(TEXT("爱达人!")));
//绘制椭圆图形
Ellipse(hDC, 250, 250, 1200, 500);
//绘制格式化文本,客户区中间垂直居中对齐
DrawText(hDC, TEXT("我的第一个窗口程序!"), -1, &rect,DT_CENTER |
DT_VCENTER | DT_SINGLELINE);
EndPaint(hwnd, &ps);
break;
case WM_DESTROY://处理退出消息
//绘制椭圆图形
//Ellipse(hDC, 250, 250, 1200, 500);//测试
PostQuitMessage(0);//此消息直接进入消息队列的头部!默认情况下,
//DefWindowProc函数调用DestroyWindow函数来销毁窗口。
break;
//default: //可以放在最后
//return DefWindowProc(hwnd, message, wParam, lParam);
}
//return 0;
//调用默认窗口过程以为应用程序未处理的任何窗口消息提供默认处理
return DefWindowProc(hwnd, message, wParam, lParam);
}
/**************注意*******************
/*
WM_DESTROY消息:窗口销毁后(调用DestroyWindow()后),消息队列添加WM_DESTROY消息。
流程:用户通过点击关闭程序按钮后,消息队列增加一条消息WM_CLOSE,然后程序从消息队列中取走WM_CLOSE,WM_CLOSE消息的默认处理方式为调用DestroyWindow()向窗口发送一个WM_DESTROY消息,消息队列增加WM_DESTROY,主程序消息循环再次取出后交给Windows系统,Windows系统调用窗口过程处理WM_DESTROY消息,处理方式为调用ostQuitMessage(),在消息队列中添加WM_QUIT标记,消息循环获取WM_QUIT后退出消息循环,程序退出。
如果窗口过程不处理WM_CLOSE消息,默认情况下,默认窗口过程DefWindowProc函数同样是调用DestroyWindow函数。
********************************************
PostQuitMessage函数:向系统指示线程已请求终止(退出)。通常用于响应WM_DESTROY消息。
void PostQuitMessage(
int nExitCode //指定的退出码,是一个整数值。这个退出码通常用来表示程序执行的结果或状态。此值用作WM_QUIT消息的wParam参数。
);
备注
PostQuitMessage函数添加一个WM_QUIT退出标记到线程的消息队列并立即返回;这个函数只是向系统表明这个线程在将来的某个时候请求退出。
当线程从它的消息队列中检索WM_QUIT标记时,它应该退出它的消息循环并将控制权返回给系统。返回到系统的退出值必须是WM_QUIT消息的wParam参数。
(有些书上将WM_QUIT视为一个消息,插入到消息队列头,然后立即退出,消息队列中剩余的消息不会被处理,虽然比较形象,但是不太严谨,WM_QUIT其实是一个退出标记。)。
*/
注意
1.本例在关闭窗口后,终于可以正常退出程序了。原因就在于窗口过程WndProc处理了WM_DESTROY消息,向窗口消息队列添加一个WM_QUIT退出标记。消息循环GetMessage函数获取WM_QUIT后返回值为0,退出消息循环,进而退出进程。
2.动手实验:在WM_DESTROY消息处理模块添加一个绘制椭圆的函数:
Ellipse(hDC, 250, 250, 1200, 500);
编译器编译后提示:error C4700: 使用了未初始化的局部变量“hDC”。
错误原因:窗口过程中设置的hDC为局部变量,在WM_PAINT消息模块中使用,但是在WM_DETROY模块无法识别,说明局部变量在窗口过程中不可以跨消息模块使用,作用域仅限一个消息模块内。记得水面以下的冰山部分并不是由我们实现的,Windows程序是消息驱动的程序。
解决方案:将变量定义为全局区的静态变量static HDC hDC;如果一个变量需要跨消息模块使用,请将其定义为static变量。但是在这里并不建议将hDC定义为static变量,因为hDC会持续占用大块内存空间,随用随取就可以了。
在Ellipse函数处下断点,当关闭窗口后,执行Ellipse函数调用并不会显示窗口。原因很简单,因为窗口已经不存在了。
2.2.8 第15练:处理WM_CLOSE消息
/*----------------------------------------
015 编程达人win32 API每日一练
第15个例子WM_CLOSE.C:回调函数---处理WM_CLOSE消息
WM_CLOSE消息
DestroyWindow函数
注意:关闭窗口后,程序退出。
(c) www.bcdaren.com 编程达人
----------------------------------*/
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //消息回调函数
/**************程序入口 **********
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
static TCHAR szAppName[] = TEXT("HelloWin"); // 窗口类名
……(略)
return msg.wParam;//msg.wParam 来自一条表示退出的消息,返回这个值给系统
}
/************回调函数——消息处理过程************/
//窗口回调函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hDC; //设备环境——绘图的地方
PAINTSTRUCT ps; //绘图结构体变量
RECT rect; //绘图区范围
switch (message)
{
case WM_CREATE: //创建窗口消息
return 0;
case WM_PAINT://绘图函数,在窗口上画画儿!
hDC = BeginPaint(hwnd, &ps);
//获得客户区大小
GetClientRect(hwnd, &rect);
//绘制字符串
TextOut(hDC, 200, 200, TEXT("爱达人!"), lstrlen(TEXT("爱达人!")));
//绘制椭圆图形
Ellipse(hDC, 250, 250, 1200, 500);
//绘制格式化文本,客户区中间垂直居中对齐
DrawText(hDC, TEXT("我的第一个窗口程序!"), -1, &rect,DT_CENTER |
DT_VCENTER | DT_SINGLELINE);
EndPaint(hwnd, &ps);
return 0;
case WM_CLOSE:
/*
1、一般是响应WM_CLOSE:调用DestroyWindow(),DestroyWindow()又发送WM_DESTROY消息;响应WM_DESTROY时,PostQuitMessage()函数发送WM_QUIT标记到消息队列,GetMessage()发现WM_QUIT时,退出程序
2、一个窗口或者应用程序应该被关闭时发出WM_CLOSE消息,当接收到WM_CLOSE消息时,
如果你愿意,可以通过MessageBox向用户提出是否真的要退出。
3、用户选择退出或不退出。*/
if (IDYES == MessageBox(hwnd,TEXT("是否真的要退
出!),TEXT("OK?"),MB_YESNO))
{
DestroyWindow(hwnd);
break;
}
else
break;//不退出,并且什么都没做。
case WM_DESTROY://处理退出消息
PostQuitMessage(0);//此消息直接进入消息队列的头部!
break;
default: //可以放在最后
return DefWindowProc(hwnd, message, wParam, lParam); //调用默认窗口过程以为应用程序未处理的任何窗口消息提供默认处理
}
return 0;
//return DefWindowProc(hwnd, message, wParam, lParam); //调用默认窗口过程以为应用程序未处理的任何窗口消息提供默认处理
}
/***************注意***********
WM_CLOSE消息:作为窗口或应用程序应终止的信号发送。
#define WM_CLOSE 0x0010
返回值类型:LRESULT
如果应用程序处理此消息,则应返回零。
备注
应用程序可以在销毁窗口之前提示用户进行确认,方法是处理WM_CLOSE消息并仅在用户确认选择后才调用DestroyWindow函数。
默认情况下,DefWindowProc函数调用DestroyWindow函数来销毁窗口。
***********************************
DestroyWindow函数:销毁指定的窗口。该函数将WM_DESTROY和WM_NCDESTROY消息发送到窗口以将其停用并从中移出键盘焦点。
该函数还破坏窗口的菜单,刷新线程消息队列,破坏计时器,删除剪贴板所有权,并中断剪贴板查看器链(如果窗口位于查看器链的顶部)。
如果指定的窗口是父窗口或所有者窗口,则DestroyWindow在销毁父窗口或所有者窗口时会自动销毁关联的子窗口或所有者窗口。该函数首先销毁子窗口或所有者窗口,然后销毁父窗口或所有者窗口。DestroyWindow还会破坏由CreateDialog函数创建的无模式对话框。
BOOL DestroyWindow(
HWND hWnd //销毁窗口的句柄
);
返回值类型:布尔
如果函数成功,则返回值为非零。
如果函数失败,则返回值为零。要获取扩展的错误信息,请调用GetLastError。
*/
点击“关闭”系统菜单后,弹出对话框窗口,如图2-6所示:
注意
读者可能会有疑问,当用户点击系统菜单“关闭”窗口时,是如何产生并获取WM_CLOSE消息的呢?答案是Windows操作系统获取“关闭”系统菜单消息后,再发送一个WM_CLOSE消息。这个问题,我们将在下一节消息机制中详细讲解。
2.2.9 第16练:处理WM_SIZE消息
/*-----------------------------
016 编程达人win32 API每日一练
第16个例子WM_SIZE.C:窗口过程---处理WM_SIZE消息
WM_SIZE消息---调整客户区大小
InvalidateRect函数
注意:关闭窗口后,程序退出。
(c) www.bcdaren.com 编程达人
-------------------------------*/
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //消息回调函数
/************程序入口 *************
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
static TCHAR szAppName[] = TEXT("HelloWin"); // 窗口类名
……(略)
return msg.wParam;//msg.wParam 来自一条表示退出的消息,返回这个值给系统
}
/*********回调函数——消息处理过程***********/
//窗口过程回调函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hDC; //设备环境——绘图的地方
PAINTSTRUCT ps; //绘图结构体变量
RECT rect; //绘图区范围
static int cxClient,cyClient;//客户区的宽度和高度,定义为静态局部变量
switch (message)
{
case WM_CREATE: //创建窗口消息
//return 0;
break; //也可以
case WM_SIZE://更改窗口客户区大小的消息
GetClientRect(hwnd, &rect);//等价于lParam参数的作用
InvalidateRect(hwnd, &rect, TRUE);//TRUE重绘窗口客户区背景
cxClient = LOWORD(lParam);//客户区的新宽度
cyClient = HIWORD(lParam);//客户区的新高度
break;
case WM_PAINT://绘图函数,在窗口上画画儿!
hDC = BeginPaint(hwnd, &ps);
//获得客户区大小
GetClientRect(hwnd, &rect);
//绘制字符串---跟随客户区尺寸的变化而变化
//TextOut(hDC, 200, 200, TEXT("爱达人!"), lstrlen(TEXT("爱达人!
")));//对比测试
TextOut(hDC, cxClient / 5, cyClient / 5, TEXT("爱达人!"),
lstrlen(TEXT("爱达人!")));
//绘制椭圆图形---跟随客户区尺寸的变化而变化
//Ellipse(hDC, 250, 250, 1200, 500);//对比测试
Ellipse(hDC, cxClient / 4 , cyClient / 4 , cxClient / 4 * 3,
cyClient / 4 * 3);
//绘制格式化文本,客户区中间垂直居中对齐
DrawText(hDC, TEXT("我的第一个窗口程序!"), -1, &rect,DT_CENTER |
DT_VCENTER | DT_SINGLELINE);
EndPaint(hwnd, &ps);
break;
case WM_CLOSE:
if (IDYES == MessageBox(hwnd,TEXT("是否真的要退出!
"),TEXT("OK?"),MB_YESNO))
{
DestroyWindow(hwnd);
break;
}
else
break;//不退出,并且什么都没做。
case WM_DESTROY://处理退出消息
PostQuitMessage(0);//此消息直接进入消息队列的头部!
break;
default: //可以放在最后
return DefWindowProc(hwnd, message, wParam, lParam);//调用默认窗口过程
}
return 0;
//return DefWindowProc(hwnd, message, wParam, lParam); //调用默认窗口过程
}
/*******************注意***************
WM_SIZE消息:当主窗口的客户区部分大小改变时,我们的应用程序将接收到 WM_SIZE 消息。
lParam 的高字部分是客户区的高,低字部分是客户区的宽。
wParam请求的调整大小类型。此参数可以是下列值之一。
SIZE_MAXHIDE:当某个其他窗口最大化时,消息将发送到所有弹出窗口。
SIZE_MAXIMIZED:窗口已最大化。
SIZE_MAXSHOW:当某些其他窗口恢复到原来的大小时,消息将发送到所有弹出窗口。
SIZE_MINIMIZED:窗口已最小化。
SIZE_RESTORED:窗口已调整大小,但SIZE_MINIMIZED或SIZE_MAXIMIZED值均不适用。
说明:
lParam和GetClientRect的功能一样,有时候WM_SIZE的效率要比使用GetClientRect高.
可以在程序中使用WM_SIZE来保存客户区的大小方便以后使用.
*******************************************
InvalidateRect函数:向指定的窗体更新区域添加一个矩形,然后窗口客户区域的这一部分将被重新绘制。
BOOL InvalidateRect(
HWND hWnd, //新区域已更改的窗口的句柄。如果此参数为NULL,
//则系统将使所有窗口(不仅是此应用程序的窗口)无效并重新绘制
//所有窗口,并在函数返回之前发送WM_ERASEBKGND和WM_NCPAINT消
//息。不建议将此参数设置为NULL。
const RECT *lpRect,//指向RECT结构的指针,该结构包含要添加到更新区域的矩形的客
//户坐标。如果此参数为NULL,则整个工作区将添加到更新区域。
BOOL bErase //指定在处理更新区域时是否要擦除更新区域内的背景。
//如果此参数为TRUE,则在调用BeginPaint函数时将擦除背景。
//如果此参数为FALSE,则背景保持不变。
);
*/ 运行结果:
注意
1.读者是否还记得,当我们执行ShowWindow函数时会向消息队列发送一个WM_SIZE消息。WM_SIZE消息用于向窗口发送尺寸改变的消息。当窗口的大小发生变化时,系统会生成并发送WM_SIZE消息给窗口,使程序能够对窗口尺寸的改变做出响应。WM_SIZE消息的lParam 参数的高字部分是客户区的高,低字部分是客户区的宽。wParam参数为请求的调整大小类型。每个Windows消息都有附属的wParam参数和lParam 参数。不同消息的wParam参数和lParam 参数的含义是不同的。我们将在2.4节中详细讲解。
2.细心的读者会发现,本例中的窗口是可以通过鼠标拖动改变窗口的大小,同时窗口客户区绘制的图形和文本也随之而改变相应的位置。请注意处理WM_PAINT消息时的GDI绘图函数,绘制图形和文本的坐标位置是和窗口客户区等比例的。首先定义static变量cxClient,cyClient,接着在处理WM_SIZE消息时通过lParam参数获取窗口客户区的高和宽。当然也可以通过GetClientRect函数获取窗口客户区的宽和高,使用其中一个方法就可以了。
3.请读者对比测试一下,如果将绘制文本和椭圆的坐标和区域设置为固定值,显示效果如何。
相关推荐
- 快递查询教程,批量查询物流,一键管理快递
-
作为商家,每天需要查询许许多多的快递单号,面对不同的快递公司,有没有简单一点的物流查询方法呢?小编的回答当然是有的,下面随小编一起来试试这个新技巧。需要哪些工具?安装一个快递批量查询高手快递单号怎么快...
- 一键自动查询所有快递的物流信息 支持圆通、韵达等多家快递
-
对于各位商家来说拥有一个好的快递软件,能够有效的提高自己的工作效率,在管理快递单号的时候都需要对单号进行表格整理,那怎么样能够快速的查询所有单号信息,并自动生成表格呢?1、其实方法很简单,我们不需要一...
- 快递查询单号查询,怎么查物流到哪了
-
输入单号怎么查快递到哪里去了呢?今天小编给大家分享一个新的技巧,它支持多家快递,一次能查询多个单号物流,还可对查询到的物流进行分析、筛选以及导出,下面一起来试试。需要哪些工具?安装一个快递批量查询高手...
- 3分钟查询物流,教你一键批量查询全部物流信息
-
很多朋友在问,如何在短时间内把单号的物流信息查询出来,查询完成后筛选已签收件、筛选未签收件,今天小编就分享一款物流查询神器,感兴趣的朋友接着往下看。第一步,运行【快递批量查询高手】在主界面中点击【添...
- 快递单号查询,一次性查询全部物流信息
-
现在各种快递的查询方式,各有各的好,各有各的劣,总的来说,还是有比较方便的。今天小编就给大家分享一个新的技巧,支持多家快递,一次能查询多个单号的物流,还能对查询到的物流进行分析、筛选以及导出,下面一起...
- 快递查询工具,批量查询多个快递快递单号的物流状态、签收时间
-
最近有朋友在问,怎么快速查询单号的物流信息呢?除了官网,还有没有更简单的方法呢?小编的回答当然是有的,下面一起来看看。需要哪些工具?安装一个快递批量查询高手多个京东的快递单号怎么快速查询?进入快递批量...
- 快递查询软件,自动识别查询快递单号查询方法
-
当你拥有多个快递单号的时候,该如何快速查询物流信息?比如单号没有快递公司时,又该如何自动识别再去查询呢?不知道如何操作的宝贝们,下面随小编一起来试试。需要哪些工具?安装一个快递批量查询高手快递单号若干...
- 教你怎样查询快递查询单号并保存物流信息
-
商家发货,快递揽收后,一般会直接手动复制到官网上一个个查询物流,那么久而久之,就会觉得查询变得特别繁琐,今天小编给大家分享一个新的技巧,下面一起来试试。教程之前,我们来预览一下用快递批量查询高手...
- 简单几步骤查询所有快递物流信息
-
在高峰期订单量大的时候,可能需要一双手当十双手去查询快递物流,但是由于逐一去查询,效率极低,追踪困难。那么今天小编给大家分享一个新的技巧,一次能查询多个快递单号的物流,下面一起来学习一下,希望能给大家...
- 物流单号查询,如何查询快递信息,按最后更新时间搜索需要的单号
-
最近有很多朋友在问,如何通过快递单号查询物流信息,并按最后更新时间搜索出需要的单号呢?下面随小编一起来试试吧。需要哪些工具?安装一个快递批量查询高手快递单号若干怎么快速查询?运行【快递批量查询高手】...
- 连续保存新单号功能解析,导入单号查询并自动识别批量查快递信息
-
快递查询已经成为我们日常生活中不可或缺的一部分。然而,面对海量的快递单号,如何高效、准确地查询每一个快递的物流信息,成为了许多人头疼的问题。幸运的是,随着科技的进步,一款名为“快递批量查询高手”的软件...
- 快递查询教程,快递单号查询,筛选更新量为1的单号
-
最近有很多朋友在问,怎么快速查询快递单号的物流,并筛选出更新量为1的单号呢?今天小编给大家分享一个新方法,一起来试试吧。需要哪些工具?安装一个快递批量查询高手多个快递单号怎么快速查询?运行【快递批量查...
- 掌握批量查询快递动态的技巧,一键查找无信息记录的两种方法解析
-
在快节奏的商业环境中,高效的物流查询是确保业务顺畅运行的关键。作为快递查询达人,我深知时间的宝贵,因此,今天我将向大家介绍一款强大的工具——快递批量查询高手软件。这款软件能够帮助你批量查询快递动态,一...
- 从复杂到简单的单号查询,一键清除单号中的符号并批量查快递信息
-
在繁忙的商务与日常生活中,快递查询已成为不可或缺的一环。然而,面对海量的单号,逐一查询不仅耗时费力,还容易出错。现在,有了快递批量查询高手软件,一切变得简单明了。只需一键,即可搞定单号查询,一键处理单...
- 物流单号查询,在哪里查询快递
-
如果在快递单号多的情况,你还在一个个复制粘贴到官网上手动查询,是一件非常麻烦的事情。于是乎今天小编给大家分享一个新的技巧,下面一起来试试。需要哪些工具?安装一个快递批量查询高手快递单号怎么快速查询?...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- wireshark怎么抓包 (75)
- qt sleep (64)
- cs1.6指令代码大全 (55)
- factory-method (60)
- sqlite3_bind_blob (52)
- hibernate update (63)
- c++ base64 (70)
- nc 命令 (52)
- wm_close (51)
- epollin (51)
- sqlca.sqlcode (57)
- lua ipairs (60)
- tv_usec (64)
- 命令行进入文件夹 (53)
- postgresql array (57)
- statfs函数 (57)
- .project文件 (54)
- lua require (56)
- for_each (67)
- c#工厂模式 (57)
- wxsqlite3 (66)
- dmesg -c (58)
- fopen参数 (53)
- tar -zxvf -c (55)
- 速递查询 (52)