百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分析 > 正文

2.2.7 Windows程序关闭窗口的实现过程

liebian365 2024-10-26 12:59 23 浏览 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.请读者对比测试一下,如果将绘制文本和椭圆的坐标和区域设置为固定值,显示效果如何。

相关推荐

go语言也可以做gui,go-fltk让你做出c++级别的桌面应用

大家都知道go语言生态并没有什么好的gui开发框架,“能用”的一个手就能数的清,好用的就更是少之又少。今天为大家推荐一个go的gui库go-fltk。它是通过cgo调用了c++的fltk库,性能非常高...

旧电脑的首选系统:TinyCore!体积小+精简+速度极快,你敢安装吗

这几天老毛桃整理了几个微型Linux发行版,准备分享给大家。要知道可供我们日常使用的Linux发行版有很多,但其中的一些发行版经常会被大家忽视。其实这些微型Linux发行版是一种非常强大的创新:在一台...

codeblocks和VS2019下的fltk使用中文

在fltk中用中文有点问题。英文是这样。中文就成这个样子了。我查了查资料,说用UTF-8编码就行了。edit->Fileencoding->UTF-8然后保存文件。看下下边的编码指示确...

FLTK(Fast Light Toolkit)一个轻量级的跨平台Python GUI库

FLTK(FastLightToolkit)是一个轻量级的跨平台GUI库,特别适用于开发需要快速、高效且简单界面的应用程序。本文将介绍Python中的FLTK库,包括其特性、应用场景以及如何通过代...

中科院开源 RISC-V 处理器“香山”流片,已成功运行 Linux

IT之家1月29日消息,去年6月份,中科院大学教授、中科院计算所研究员包云岗,发布了开源高性能RISC-V处理器核心——香山。近日,包云岗在社交平台晒出图片,香山芯片已流片,回片后...

Linux 5.13内核有望合并对苹果M1处理器支持的初步代码

预计Linux5.13将初步支持苹果SiliconM1处理器,不过完整的支持工作可能还需要几年时间才能完全完成。虽然Linux已经可以在苹果SiliconM1上运行,但这需要通过一系列的补丁才能...

Ubuntu系统下COM口测试教程(ubuntu port)

1、在待测试的板上下载minicom,下载minicom有两种方法:方法一:在Ubuntu软件中心里面搜索下载方法二:按“Ctrl+Alt+T”打开终端,打开终端后输入“sudosu”回车;在下...

湖北嵌入式软件工程师培训怎么选,让自己脱颖而出

很多年轻人毕业即失业、面试总是不如意、薪酬不满意、在家躺平。“就业难”该如何应对,参加培训是否能改变自己的职业走向,在湖北,有哪些嵌入式软件工程师培训怎么选值得推荐?粤嵌科技在嵌入式培训领域有十几年经...

新阁上位机开发---10年工程师的Modbus总结

前言我算了一下,今年是我跟Modbus相识的第10年,从最开始的简单应用到协议了解,从协议开发到协议讲解,这个陪伴了10年的协议,它一直没变,变的只是我对它的理解和认识。我一直认为Modbus协议的存...

创建你的第一个可运行的嵌入式Linux系统-5

@ZHangZMo在MicrochipBuildroot中配置QT5选择Graphic配置文件增加QT5的配置修改根文件系统支持QT5修改output/target/etc/profile配置文件...

如何在Linux下给zigbee CC2530实现上位机

0、前言网友提问如下:粉丝提问项目框架汇总下这个网友的问题,其实就是实现一个网关程序,内容分为几块:下位机,通过串口与上位机相连;下位机要能够接收上位机下发的命令,并解析这些命令;下位机能够根据这些命...

Python实现串口助手 - 03串口功能实现

 串口调试助手是最核心的当然是串口数据收发与显示的功能,pzh-py-com借助的是pySerial库实现串口收发功能,今天痞子衡为大家介绍pySerial是如何在pzh-py-com发挥功能的。一、...

为什么选择UART(串口)作为调试接口,而不是I2C、SPI等其他接口

UART(通用异步收发传输器)通常被选作调试接口有以下几个原因:简单性:协议简单:UART的协议非常简单,只需设置波特率、数据位、停止位和校验位就可以进行通信。相比之下,I2C和SPI需要处理更多的通...

同一个类,不同代码,Qt 串口类QSerialPort 与各种外设通讯处理

串口通讯在各种外设通讯中是常见接口,因为各种嵌入式CPU中串口标配,工业控制中如果不够还通过各种串口芯片进行扩展。比如spi接口的W25Q128FV.对于软件而言,因为驱动接口固定,软件也相对好写,因...

嵌入式linux为什么可以通过PC上的串口去执行命令?

1、uboot(负责初始化基本硬bai件,如串口,网卡,usb口等,然du后引导系统zhi运行)2、linux系统(真正的操作系统)3、你的应用程序(基于操作系统的软件应用)当你开发板上电时,u...

取消回复欢迎 发表评论: