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],它告诉应用程序某个特定的事件发生了.比如,用户单 ...
随机推荐
- 通过路由器的IP映射来解决,两个不同IP地址的PC机之间的从LAN口到WAN口的单向通讯问题
1.问题假设: 在B机中IP地址与子网掩码都固定,网关是路由器的LAN口的IP地址,我们希望通过路由器来实现B机与A机之间的单向通讯问题,也就是说B可以ping通A且可以访问A提供的FTP站点. 2. ...
- RabbitMQ二:AMQP协议
参考这个:http://kb.cnblogs.com/page/73759/ 参考这个:http://www.cnblogs.com/charlesblc/p/6286875.html 写的挺好 Ra ...
- 【JAVA 学习笔记2】if使用例子
int a =3; if (a%2==0) { System.out.println(a+" 是偶数"); System.out.println(a+" 不是奇数&quo ...
- js正则表达式限制文本框只能输入数字,小数点,英文字母
1.文本框只能输入数字代码(小数点也不能输入)<input onkeyup="this.value=this.value.replace(/\D/g,'')" onafter ...
- My-Eclipse 快捷键大全
Ctrl+1 快速修复(最经典的快捷键,就不用多说了) Ctrl+D: 删除当前行 Ctrl+Alt+↓ 复制当前行到下一行(复制增加) Ctrl+Alt+↑ 复制当前行到上一行(复制增加) Alt+ ...
- Java 基础入门随笔(10) JavaSE版——单例设计模式
设计模式:对问题行之有效的解决方式.其实它是一种思想. 1.单例设计模式. 解决的问题:就是可以保证一个类在内存中的对象唯一性.(单个实例) 使用单例设计模式需求:必须对于多个程序使用同一个配置信息对 ...
- Call stack Structure
The stack frame at the top of the stack is for the currently executing routine. Th ...
- MySql学习笔记(二) —— 正则表达式的使用
前面介绍利用一些关键字搭配相应的SQL语句进行数据库查找过滤,但随着过滤条件的复杂性的增加,where 子句本身的复杂性也会增加.这时我们就可以利用正则表达式来进行匹配查找. 1.基本字符匹配 ' o ...
- Linux(Centos7) 设置静态IP
关于虚拟机 这里使用Centos7为例,因为linux是安装在在虚拟机中,这里先看一下虚拟机的网络适配器: 这里我使用的NAT模式,接着配置虚拟机的虚拟网络: 这里主要看一下VMnet8的设置: 这里 ...
- 10Oracle Database 数据表数据查询
Oracle Database 数据表数据查询 DML 数据操纵语言 - 数据的查看和维护 select / insert /delete /update 基本查询语句 Select [distinc ...