Windows 消息机制
Windows 是一个消息驱动的操作系统。一个消息由一个消息名称(UINT 类型)和两个参数
(WPARAM,LPARAM)构成。当用户进行了输入或是窗口的状态发生改变时,系统会发送消息到某
一个窗口。例如当菜单选中之后会有WM_COMMAND 消息发送,WPARAM 的高字中
(HIWORD(wParam))是命令的ID 号,对菜单来说就是菜单ID。当然用户也可以定义自己的消息名称,
也可以利用自定义消息来发送通知和传送数据。
一个消息必须由一个窗口接收。在窗口的消息处理函数中可以对消息进行分析,对自己感兴趣的
消息进行处理。例如希望对菜单选择进行处理,那么可以定义对WM_COMMAND 进行处理的代码,
如果希望在窗口中进行图形输出就必须对WM_PAINT 进行处理。
事实上为了应付那些没有被响应的消息,Windows 为窗口编写了默认的窗口消息处理函数,这个
窗口过程将负责处理那些程序中没有处理的消息。正因为有了这个默认窗口过程,程序员才可以利用
Windows 的窗口进行开发而不必过多关注窗口各种消息的处理。例如窗口在被拖动时会有很多消息发
送,而程序员都可以不予理睬让系统自己去处理。
说到消息就不能不说窗口句柄,系统通过窗口句柄在整个操作系统中唯一标识一个窗口。发送消
息时必须指定一个窗口句柄表明该消息由哪个窗口接收。而每个窗口都会有自己的窗口消息处理函数,
所以用户的输入就会被正确的处理。
下面这段伪代码演示如何在窗口过程中处理消息:
LONG windowProc(HWND hWnd,UINT uMessageType,WPARAM w,LPARAM l)
{
switch(uMessageType)
{
//使用SWITCH 语句将各种消息分开
case WM_PAINT: //处理绘制消息
Redraw();
break;
case WM_TIMER: //处理定时器消息
OnTimer();
break;
case WM_LBUTTONDOWN: //处理鼠标左键按下的消息
OnLButtonDown ();
break;
default:
defaultWndProc(); //缺省的其他消息处理函数
break;
}
}
在Windows 操作系统中维护着一个或多个消息队列,所有产生的消息都被放入到队列中。系统在
队列中每次取出一条消息,根据消息的接收句柄而将该消息发送给拥有该窗口的消息循环。每一个运
行的程序都有自己的消息循环,在循环中得到属于自己的消息并根据接收窗口的句柄调用相应的窗口
过程。而在没有消息时消息循环就将控制权交给系统,从而使Windows 可以同时进行多个任务。下面
的伪代码演示了消息循环的用法:
while ()
{
id =GetMessage ();
if (id == WM_QUIT)
break ;
DispatchMessage ();
}
初看这段代码容易给人一种错觉,这是一个忙等待(busy waiting)的消息循环,因为采用了while(1)
的循环方式,而忙等待是个非常糟糕的东西。
而实际上绝大部分时间里这个程序是在阻塞状态, 因为当程序没有收到消息通知时
GetMessage 就不会返回,所以也就不会占用系统的CPU 时间。GetMessage 函数的阻塞调用是这
段代码的关键部分。
上面这段代码的意思是调用GetMessage 函数从消息队列中取出消息,然后调用DispatchMessage
将消息发送给窗口消息处理程序。
在16 位的系统中只有一个消息队列,所以系统必须等待当前任务处理消息后才可以发送下一消息
到相应程序,如果一个程序陷入死循环或是耗时操作时,系统就会得不到控制权。这种多任务系统也
称为协同式的多任务系统。Windows 3.X 就是这种系统。而32 位的系统中每一个运行的程序都会有一
个消息队列,所以系统可以在多个消息队列中转换而不必等待当前程序完成消息处理就可以得到控制
权。这种多任务系统就称为抢先式的多任务系统,Windows 95/NT 就是这种系统。
消息能够被分为队列化的和非队列化的。队列化的消息是由Windows 放入程序消息队列中的。在
程序的消息循环中,重新传回并分配给窗口消息处理程序。非队列化的消息在Windows 调用窗口时直
接送给窗口消息处理程序。也就是说,队列化的消息被发送给消息队列,而非队列化的消息则发送给
窗口消息处理程序。任何情况下,窗口消息处理程序都将获得窗口所有的消息——包括队列化的和非
队列化的。窗口消息处理程序是窗口的消息中心。
队列化消息基本上是使用者输入的结果,以击键(如WM_KEYDOWN 和WM_KEYUP 消息)、
击键产生的字符(WM_CHAR)、鼠标移动(WM_MOUSEMOVE)和鼠标按钮(WM_LBUTTONDOWN)
的形式给出。队列化消息还包含时钟消息(WM_TIMER)、更新消息(WM_PAINT)和退出消息
(WM_QUIT)。
在许多情况下,非队列化消息来自调用特定的Windows 函数。例如,当WinMain 调用CreateWindow
时,Windows 将建立窗口并在处理中给窗口消息处理程序发送一个WM_CREATE 消息。当WinMain
调用ShowWindow 时,Windows 将给窗口消息处理程序发送WM_SIZE 和WM_SHOWWINDOW消息。
虽然Windows 程序可以多线程执行,但消息队列只为窗口消息处理程序在该执行中的窗口处理消
息。换句话说,消息循环和窗口消息处理程序不是并发执行的。当一个消息循环从其消息队列中接收
一个消息,然后调用DispatchMessage 将消息发送给窗口消息处理程序时,直到窗口消息处理程序将
控制传回给Windows,DispatchMessage 才能结束执行。
当然,窗口消息处理程序能调用给窗口消息处理程序发送另一个消息的函数。这时,窗口消息处
理程序必须在函数调用传回之前完成对第二个消息的处理。那时窗口消息处理程序将处理最初的消息。
例如,当窗口过程调用UpdateWindow 时,Windows 将调用窗口消息处理程序来处理WM_PAINT 消息。
窗口消息处理程序处理WM_PAINT 消息结束以后,UpdateWindow 调用将把控制传回给窗口消息处理
程序。
在工程中经常还会有这样的需求,即要求程序在空闲的时候执行某种额外的操作或运算。实际上
在Windows 中有很多闲置时间,在这个时间内,所有消息队列为空,Windows 只停在一个小循环中等
待键盘或者鼠标输入。能否在闲置时间内获得控制,从而做某种操作或运算,并且只在有消息加入程
序的消息队列之后才释放控制呢?这就是PeekMessage 函数的目的之一。下面是PeekMessage 调用的
一个例子:
PeekMessage (&msg, NULL, , , PM_REMOVE) ;
前面的4 个参数(一个指向MSG 结构的指针、一个窗口句柄、两个值指示消息范围)与GetMessage
的参数相同。将第二、三、四个参数设定为NULL 或0 时,表明想让PeekMessage 传回程序中所有窗
口的所有消息。如果要将消息从消息队列中删除,则将PeekMessage 的最后一个参数设定为
PM_REMOVE。如果不希望删除消息,那么可以将这个参数设定为PM_NOREMOVE。
GetMessage 不将控制传回给程序,它一直处于阻塞状态,直到从程序的消息队列中取得消息,但
是PeekMessage 总是立刻传回,而不论一个消息是否出现。当消息队列中有一个消息时,PeekMessage
的传回值为TRUE,并且将按通常方式处理消息。当队列中没有消息时,PeekMessage 传回FALSE。
可以将如下所示的消息循环:
while ()
{
id =GetMessage ();
if (id == WM_QUIT)
break ;
DispatchMessage ();
}
替换为下面的循环:
while (TRUE)
{
if (PeekMessage (&msg, NULL, , , PM_REMOVE))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
else
{
// 完成其他额外的工作
}
}
return msg.wParam ;
如果PeekMessage 的传回值为TRUE,则消息按通常方式进行处理。如果传回值为FALSE,则在
将控制传回给Windows 之前,还可以做一点工作。
需要注意的是,不能用PeekMessage 从消息队列中删除WM_PAINT 消息,实际上GetMessage 并
不从消息队列中删除WM_PAINT 消息。从队列中删除WM_PAINT 消息的唯一方法是令窗口显示区域
的失效区域变得有效,这可以用ValidateRect 和ValidateRgn 或者BeginPaint 和EndPaint 对来完成。如
果在使用PeekMessage 从队列中取出WM_PAINT 消息后,同平常一样处理它,那么就不会有问题了。
所不能作的是使用如下所示的程序代码来清除消息队列中的所有消息:
while (PeekMessage (&msg, NULL, , , PM_REMOVE)) ;
这行代码从消息队列中删除WM_PAINT 之外的所有消息。如果队列中有一个WM_PAINT 消息,
程序就会永远地陷在while 循环中。
Windows 消息机制的更多相关文章
- Windows消息机制
Windows的消息系统是由3个部分组成的: · 消息队列.Windows能够为所有的应用程序维护一个消息队列.应用程序必须从消息队列中获取消息,然后分派给某个窗口.· 消息循环.通过这个循环机制应用 ...
- 我对windows消息机制的理解(参考深入浅出MFC,欢迎批评指正!!)
以消息为基础,以事件驱动之 程序的进行依靠外部消息来驱动,即:程序不断等待任何可能的输入,然后做判断,然后再做适当的处理. 消息输入:操作系统捕获,以消息形式进入程序.(操作系统通过其USERS模块中 ...
- <Win32_1>深入浅出windows消息机制[转自crocodile_]
上学期学习了Java ,感觉Java写一个窗口真心简单,很易上手,也就难怪很多开发人员选择Java作为自己的开发编程语言.但是由于自身对windows的热爱,让我觉得c.c++语言才是我亲睐的编程语言 ...
- 深入Delphi -- Windows 消息机制
http://www.txsz.net/xs/delphi/3/Windows%20%E6%B6%88%E6%81%AF%E6%9C%BA%E5%88%B6.htm Windows 消息机制 by m ...
- 收藏:Windows消息机制
百度百科介绍的windows消息机制也不错:http://baike.baidu.com/view/672379.htm Windows的应用程序一般包含窗口(Window),它主要为用户提供一种可视 ...
- 转:Windows消息机制要点
Windows消息机制要点 1. 窗口过程 每个窗口会有一个称为窗口过程的回调函数(WndProc),它带有四个参数,分别为:窗口句柄(Window Handle),消息ID(Message ...
- windows消息机制(转)
1. 引言Windows 在操作系统平台占有绝对统治地位,基于Windows 的编程和开发越来越广泛.Dos 是过程驱动的,而Windows 是事件驱动的[6],这种差别的存在使得很多Dos 程序员不 ...
- 深入理解windows 消息机制
深入理解Windows消息机制 今天我们来学一学Windows消息机制,我们知道在传统的C语音程序中,当我们需要打开一个文件时,我们可以调用fopen()函数,这个函数最后又会调用操作系统提供的函数以 ...
- windows消息机制框架原理【简单版本】
windows消息机制框架原理 结合两张图理解 窗口和窗口类 Windows UI 应用程序 (e) 具有一个主线程 (g).一个或多个窗口 (a) 和一个或多个子线程 (k) [工作线程或 UI 线 ...
- Windows消息机制详解
消息是指什么? 消息系统对于一个win32程序来说十分重要,它是一个程序运行的动力源泉.一个消息,是系统定义的一个32位的值,他唯一的定义了一个事件,向 Windows发出一个通知,告诉应用 ...
随机推荐
- 8张图带你深入理解Java
1.字符串的不变性 下图展示了如下的代码运行过程: String s = "abcd";s = s.concat("ef"); 备注:String refe ...
- 报错:java.io.FileNotFoundException: (系统找不到指定的路径。)
报错如下: java.io.FileNotFoundException: E:\apache-tomcat-8.0.37\webapps\20161028-FileUpLoad\WEB-INF\fil ...
- item30,最小的k个数
剑指offer给出两类方法: 1,借助快排的思想,需要修改输入数组的元素,时间复杂度O(n) 2,借助STL中set或者multiset,因为它们的底层数据结构是红黑树实现的,插入数据时间复杂度为O( ...
- ActionScript 3.0 编程精髓 示例源码下载
根据书籍介绍(http://product.china-pub.com/38852#qy)的指引,找到了下载地址:http://moock.org/eas3/examples/
- storm的作业单元:Topology
Storm系统的数据处理应用单元,是被打包的被称为Topology的作业. 它是由多个数据处理阶段组合而成的,而每个处理阶段在构造时被称为组件(Component),在运行时被称为任务. 那么,组件根 ...
- DBA_在Linux上安装Oracle Database11g数据库(案例)
2014-08-08 Created By BaoXinjian
- poj 1011 Sticks
Sticks Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 126238 Accepted: 29477 Descrip ...
- JAVA 综合布局应用
//布局综合应用 import java.awt.*; import javax.swing.*; public class Jiemian4 extends JFrame{ JPanel mb1,m ...
- MSSQL死锁产生原因及解决方法
一. 什么是死锁 死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等的进 ...
- Spring Bean 生命周期2
在spring中,从BeanFactory或ApplicationContext取得的实例为Singleton,也就是预设为每一个Bean的别名只能维持一个实例,而不是每次都产生一个新的对象使用Sin ...