Win32编程API 基础篇 -- 4.消息循环
消息循环
理解消息循环
为了编写任何即使是最简单的程序,了解windows程序的消息循环和整个消息发送结构是非常有必要的。既然我们已经尝试了一点消息处理的东西,我们应该对整个程序有更深入的理解,如果你没有理解消息是怎么发生的和它们运行的机制,那接下来的内容你会感到很蛋疼。
什么是消息?
一条消息是一个整数值,如果你查阅你的头文件(这是个好的查阅API的工作惯例)你会发现像下面的东西:
#define WM_INITDIALOG 0x0110
#define WM_COMMAND 0x0111 #define WM_LBUTTONDOWN 0x0201
还有很多类似的东西。在windows中消息几乎被运用在基本水平上的所有通信,如果你想要一个窗口控制(只是一个制定的窗口)做某些事情你需要发送给它一条消息;同样的如果另一个窗口想让你做某些事情,它会发送给你一条消息;如果某些像用户点击键盘、移动鼠标、点击按钮的事件发生,那么相应的消息会被系统发送到受影响的窗口,如果你是其中一个受影响的窗口,那么你需要处理消息并采取相应的行动。
每条窗口消息都可能有两个参数,wParam和lParam,最初wParam是16位而lParam是32位,但是在win32中它们都是32位。不是每条消息都需要用到这些参数,用到时的用法也不同,举个例子,WM_CLOSE消息就都没有用到这两个参数,这时我们可以忽略这两个采纳数;而WM_COMMAND就同时用到了这两个参数,wParam包含了两个值,HIWORD(wParam)是一条通知消息(如果可用的话),LOWORD(wParam)是一个发送消息的控制或菜单id。
lParam是控制发送消息或NULL(如果消息不是从控制中来的话)的HWND(窗口句柄)
HIWORD()和LOWORD()被windows宏定义,这两个值都是windows中的宏定义,一个指32位(0xFFFF0000)的高16位(FFFF),一个指32位的低16位(0000);在win32中一个WORD表示的是16位,而DWORD(double word)才表示32位
你可以通过PostMessage()或SendMessage()这两种方式发送消息。PostMessage()将消息插入到消息队列中然后马上返回,这意味着即使一次PostMessage()的调用被完成时,消息可能还没被处理;SendMessage()直接发送消息给窗口并且会不会立即返回,知道窗口完成了对消息的处理。如果我们想关闭一个窗口,我们可以通过PostMessage(hwnd, WM_CLOSE, 0, 0)发送一个WM_CLOSE的消息,这跟我们点击窗口右上方的X按钮有同样的效果,注意在WM_CLOSE消息的相应处理中,wParam和lParam都是0,这是因为刚才提到的在WM_CLOSE消息处理中没有用到这两个参数。
对话框
一旦你开始使用对话框盒子,为了跟它们交流你将需要发送消息给控制。你可以通过使用GetDlgItem()通过使用ID先获取控制的句柄然后使用SendMessage(),或者你可以使用SendDlgItemMessage(),这个方法组合了上面的两个步骤。你给它一个窗口句柄和孩子ID将会得到一个孩子句柄,然后向他发送消息。SendDlgItemMessage()和类似的APIGetDlgItemText()将会作用在所有的窗口上,而不仅仅是对话框。
什么是消息队列?
让我们试着想象当你忙着处理WM_PAINT的时候突然用户用键盘输入一堆东西,会发生什么事情?你绘图的时候被键盘打断应该抛弃吗?当然不是!很明显不管抛弃哪个都不是正确的做法,所以我们有了消息队列。新增POST过来的消息会被添加到消息队列中来,当消息被处理时消息就会从消息队列中被移除,这确保了你不会错过一条消息,如果你正在处理一条消息,那么其他消息就会在队列中等待直到你开始处理它们。
什么是一个消息环?
while(GetMessage(&Msg, NULL, , ) > )
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
1、在消息队列里,消息环调用GetMessage()方法,如果你的消息队列空了,那么你的程序
将会停止然后等待消息,相当于挂起的状态
2、当一个事件发生时,会导致一条消息加入到消息队列中(比如系统注册一个鼠标点击)
GetMessage()返回一个正值表明有一个消息要处理,并且复制给我们传递给它的MSG数
据结构的成员,方法也可能会返回0如果消息是WM_QUIT,如果是发生错误的话方法会
返回负数。
3、我们得到消息(在Msg变量中)然后把它传递给TranslateMessage(),这会产生一点额外
的处理,将虚拟的键盘点击翻译成字符消息,这个步骤实际上是可选的,如果不需要时
它不会发生。
4、一旦上面的步骤完成,我们把消息传递给DispatchMessage(),DispatchMessage()会做的
工作就是拿到消息,检查它是哪个窗口的然后找到对应窗口的消息处理程序,接着调用
消息处理程序,并且将窗口的句柄,消息,wParam和lParam四个参数传递给它。
5、在你的消息处理程序中,你会检查参数消息,然后对它做任何你想做的事情,如果你没
有处理指定的消息,那么默认它会调用DefWindowProc()方法,这个消息处理的默认操作,
通常意味着什么都不做。
6、一旦你已经完成了消息处理,你的消息处理程序会返回,DisPatchMessage()返回,然后
我们又回到了环开始的地方。
这是窗口程序中一个非常重要的概念,你的消息处理程序不是神奇地由系统调用,实际上你是间接地通过DispatchMessage()调用。如果你想要的话,你可以在窗口句柄中使用GetWindowLong()直接调用窗口处理程序。
while(GetMessage(&Msg, NULL, , ) > )
{
WNDPROC fWndProc = (WNDPROC)GetWindowLong(Msg.hwnd, GWL_WNDPROC);
fWndProc(Msg.hwnd, Msg.message, Msg.wParam, Msg.lParam);
}
我试着用前面的代码,但是并没有正常工作,因为有各种各样的问题比如Unicode/Ansi编码翻译等,调用定时器回调方法不会占用,这可能会打破我们目前的所有但微不足道的程序,所以你可以尝试,但在实际的代码中不要真的这么做: )
注意,我们使用GetWindowLong()来检索窗口相关的消息处理程序,为什么我们不直接调用WndProc()呢?嗯,我们的消息环是要对我们程序中所有窗口都适用的,包括有自己的窗口过程的按钮和列表框,所以我们应该保证调用了正确的窗口过程,因为多个窗口可以使用同一个窗口过程,所以第一个参数(窗口句柄)用来告诉窗口过程消息属于哪个窗口的。
正如你所看到的,你的应用程序的大多数时间都花费在执行这个消息循环,你可以愉悦地发送消息给可以处理它们的快乐的窗口。但是当你想退出程序时你会怎么做?因为我们使用了一个while()循环,如果GetMessage()返回了false,循环将会结束并且到达我们WinMain()的终点接着退出程序。这正是PostQuitMessage()完成的东西,它把WM_QUIT消息放入队列中而不是返回一个正值,GetMessage()会填充Msg数据结构的内容然后返回0,在这个时候Msg的wParam成员就包含了你传递给PostQuitMessage()的值,这里你可以忽略它,从WinMain()的返回值将会使用在进程结束的退出代码中。
重点:GetMessgae()将会返回-1如果出现错误的话,确保记住这个,否则在某些时候会让你很蛋疼。。。即使GetMessage()被定义用来返回一个布尔值,它也能够返回TRUE或FALSE之外的值,因为BOOL使用UINT(unsigned int)来定义的。下面的代码例子可能可以运行,但是不会正确地处理一下条件:
while(GetMessage(&Msg, NULL, , ))
while(GetMessage(&Msg, NULL, , ) != )
while(GetMessage(&Msg, NULL, , ) == TRUE)
上面都是错的!就像我刚刚提到的,它可能跟我第一个教程里使用的差不多,只要GetMessage()没有失败它会工作地很好,即使你的代码是正确的,这个也不是正确的,但是在很多情况下这个代码是错误的不能正常工作,当GetMessage()发生失败的时候!这里我们郑重说明和纠正,请原谅我错过了一些要点。
while(GetMessage(&Msg, NULL, , ) > )
这个才是正确的!
我希望现在你对窗口消息循环有更好的理解,如果还不是非常理解的话,别害怕,一旦你使用它们一段时间之后情况会好很多。
PS.由于本人英文水平所限,只能翻译到这个程度了,有纰漏还望多多指出,附上本篇翻译的英文原版教程地址:http://www.winprog.org/tutorial/message_loop.html
Win32编程API 基础篇 -- 4.消息循环的更多相关文章
- Win32编程API 基础篇 -- 1.入门指南 根据英文教程翻译
入门指南 本教程是关于什么的 本教程的目的是向你介绍使用win32 API编写程序的基础知识(和通用的写法).使用的语言是C,但大多数C++编译器也能成功编译,事实上,教程中的绝大多数内容都适用于任何 ...
- Win32编程API 基础篇 -- 3.消息处理 根据英文教程翻译
消息处理 例子:窗口点击 好的,现在我们已经得到一个窗口了,但我们什么也做不了除了DefWindowProc()允许窗口大小被调整,最大最小化等...这不是很激动人心啊 在接下来的一小节中我将向你展示 ...
- Win32编程API 基础篇 -- 6.菜单和图标
菜单和按钮 例子:菜单1 本小节仅仅向你展示如果向你的窗口中加入一个基本的菜单,通常你会用到一个提前制作好的菜单资源,这会是一份.rc文件并且会被编译链接进你的.exe可执行程序中.这是具体的流程做法 ...
- Win32编程API 基础篇 -- 2.一个简单的窗口 根据英文教程翻译
一个简单的窗口 例子:简单的窗口 有时人们在IRC提问,”我应该怎样制作一个窗口”...嗯,这恐怕不是完全这么简单好回答!其实这并不难一旦你明白你在做什么,但在你得到一个可展示的窗口之前还有一些事情需 ...
- Win32编程API 基础篇 -- 5.使用资源
使用资源 你可能想参考教程结尾的附近,为了获得跟VC++和BC++资源相关的信息. 在我们讲得更加深入之前,我将大致讲解一下资源的主题,这样在每个小节中我就不必再去重讲一遍了.在这一小节中,你不需要编 ...
- 【TCP/IP】之Java socket编程API基础
Socket是Java网络编程的基础,深入学习socket对于了解tcp/ip网络通信协议很有帮助, 此文讲解Socket的基础编程.Socket用法:①.主要用在进程间,网络间通信. 文章目录如下: ...
- ASP.NET Web API 基础篇1
ASP.NET Web API 直到我膝盖中了一箭[1]基础篇 无题 蓦然回首,那些年,我竟然一直很二. 小时候,读武侠小说的时候,看到那些猪脚,常常会产生一种代入感,幻想自己也会遭遇某种奇遇,遇到悬 ...
- (转)Android高性能编程(1)--基础篇
关于专题 本专题将深入研究Android的高性能编程方面,其中涉及到的内容会有Android内存优化,算法优化,Android的界面优化,Android指令级优化,以及Android应用内存占 ...
- 基础篇-Windows消息机制
1在介绍Windows 消息运行机制之前,首先介绍一下消息的概念: 消息(Message)指的就是Windows 操作系统发给应用程序的一个通告[5],它告诉应用程序某个特定的事件发生了.比如,用户单 ...
随机推荐
- c++ string函数合集
s.substr(x,len) 在s中取出从x位置开始,长度为len的字符串,并返回string类型的字符串. s.find(a) 在s中查找字符串a,并返回起始下标(从0开始),若不存在,返回184 ...
- Light 1289 - LCM from 1 to n (位图标记+素数筛选)
题目链接: http://www.lightoj.com/volume_showproblem.php?problem=1289 题目描述: 给出一个n,求出lcm(1,2,3......n)为多少? ...
- 贪心 Codeforces Round #135 (Div. 2) C. Color Stripe
题目传送门 /* 贪心:当m == 2时,结果肯定是ABABAB或BABABA,取最小改变量:当m > 2时,当与前一个相等时, 改变一个字母 同时不和下一个相等就是最优的解法 */ #incl ...
- Windows 7上安装Microsoft Loopback Adapter(微软环回网卡)
Oracle 安装过程中,先决条件检查遇到如下错误: 正在检查网络配置要求... 检查完成.此次检查的总体结果为: 失败 <<<< 问题: 安装检测到系统的主 IP 地址是 ...
- 循环语言(for)
循环语句: 给出初始条件,先判断是否满足循环条件,如果不满足条件则跳过for语句,如果满足则进入for语句循环,for语句内的代码执行完毕之后,将按照状态改变改变变量,然后判断是否符合循环条件,符合继 ...
- Java 8 (4) Stream 流 - 使用
在本节中将介绍Stream API支持的许多操作,这些操作可以完成更复杂的数据查询,如筛选.切片.映射.查找.匹配和归约.还有一些特殊的流如:数值流.来自文件和数组等多种来源的流. 筛选和切片 1.用 ...
- 6.13---example
example如何使用?简单查询这个例子展示了如何用生成后的Example类去生成一个简单的where子句: TestTableExample example = new TestTableExamp ...
- (1)指针、引用、const限定符
自己看书时的一些理解,可能有错误的地方.随着指针的使用增多,会不断修改这篇文章的内容,过去错误的会用划线划去后保留. 1.对引用.指针.常量引用.指向常量的指针.常量指针的理解 //对引用.指针.常量 ...
- 基于SOC方案的嵌入式开发-远程定时设备
Soc方案实现简单的定时开关灯 http://club.gizwits.com/forum.php?mod=viewthread&tid=7787&highlight=%E5%AE%9 ...
- oracle in表达式参数支持最大上限1000个
oracle in表达式参数支持最大上限1000个 方法是拆分为多个 col in ... or col in ... #region 解决大于1000的问题 private String getSu ...