用户是如何跟应用软件打交道的我们来看看,用户究竟是如何与应用软件打交道的(用户不需要知道这个具体过程,但应用软件的开发人员必须知道),如下图所示:从上图可以看到:在物理上,离用户最近的实际上是输入输出设备,下面我们看看上图中1-6这六个步骤分别表示什么意思(为了简便,在叙述时,我们的标号没有用圆圈):1. 用户点击鼠标或者键盘;2. Windows感觉到了鼠标或键盘的动作;3. Windows把这个消息告诉应用程序;4. 应用程序告诉Windows去做事,实际上就是应用程序调用Windows的API函数;5. Windows让输出设备做事;6. 用户获得输出。
对用户来说,没有必要了解输入输出设备和Windows的相关知识。
对程序员(写应用程序的人)来说,没有必要了解输入输出设备,但是必须了解Windows的基本知识。
在下面的叙述中,我们就不管输入输出设备了。
上面的过程还是很笼统,为了弄得更清楚,我们有必要了解Windows的消息机制,如图:下面,我们来慢慢描述(上图中的虚线表示消息的流程):step0: 程序员编程,把WinMain函数和窗口回调函数写好;step1: Windows调用WinMain函数,启动应用程序,Windows会建立一个消息队列,用来存储消息。
step2: WinMain函数调用Windows的API函数,比如调用CreateWindow和ShowWindow, 从而生成并显示一个窗口。
在调用CreateWindow函数时,会产生一个消息,这个消息并不进入消息队列,但窗口的回调函数仍然会处理,在此,我们不讨论非队列消息。
step3: WinMain函数调用Windows的API函数,比如调用GetMessage来从消息队列中取出消息。
假设用户这个时候在窗口中点击鼠标,那么Windows会把这个事件包装成消息,投到消息队列中,GetMessage会取出这个消息,通过DispatchMessage送到Windows;step4: Windows进而会将该消息发送到窗口的回调函数,并对该函数进行调用;step5:窗口的回调函数可以对这个消息进行相应处理,这个处理的具体方法由程序员自己决定,通常是调用Windows的API函数来实现处理。
Windows是一个消息(Message)驱动系统。
Windows的消息提供了应用程序之间、应用程序与Windows系统之间进行通信的手段。
应用程序想要实现的功能由消息来触发,并且靠对消息的响应和处理来完成。
必须注意的是,消息并非是抢占性的,无论事件的缓急,总是按照到达的先后派对,依次处理(一些系统消息除外),这样可能使一些实时外部事件得不到及时处理。
Windows的应用程序一般包含窗口(Window),它主要为用户提供一种可视化的交互方式,窗口是总是在某个线程(Thread)内创建的。
Windows系统通过消息机制来管理交互,消息(Message)被发送,保存,处理,一个线程会维护自己的一套消息队列(Message Queue),以保持线程间的独占性。
队列的特点无非是先进先出,这种机制可以实现一种异步的需求响应过程。
目录:1、消息2 、消息类型3 、消息队列(Message Queues)4 、队列消息(Queued Messages)和非队列消息(Non-Queued Messages)5 、PostMessage(PostThreadMessage), SendMessage6 、GetMessage, PeekMessage7 、TranslateMessage, TranslateAccelerator8、(消息死锁( Message Deadlocks)9、BroadcastSystemMessage10、消息的处理11、MFC的消息映射12、消息反射机制1、消息消息系统对于一个win32程序来说十分重要,它是一个程序运行的动力源泉。
一个消息,是系统定义的一个32位的值,他唯一的定义了一个事件,向Windows发出一个通知,告诉应用程序某个事情发生了。
例如,单击鼠标、改变窗口尺寸、按下键盘上的一个键都会使Windows发送一个消息给应用程序。
消息本身是作为一个记录传递给应用程序的,这个记录中包含了消息的类型以及其他信息。
例如,对于单击鼠标所产生的消息来说,这个记录中包含了单击鼠标时的坐标。
这个记录类型叫做MSG,MSG含有来自windows应用程序消息队列的消息信息,它在Windows中声明如下:typedefstructtagMsg{HWND hwnd; // 接受该消息的窗口句柄UINT message; // 消息常量标识符,也就是我们通常所说的消息号WPARAM wParam; // 32位消息的特定附加信息,确切含义依赖于消息值LPARAM lParam; // 32位消息的特定附加信息,确切含义依赖于消息值DWORD time; // 消息创建时的时间POINT pt; // 消息创建时的鼠标/光标在屏幕坐标系中的位置}MSG;消息可以由系统或者应用程序产生。
系统在发生输入事件时产生消息。
举个例子, 当用户敲键, 移动鼠标或者单击控件。
系统也产生消息以响应由应用程序带来的变化, 比如应用程序改变系统字体,改变窗体大小。
应用程序可以产生消息使窗体执行任务,或者与其他应用程序中的窗口通讯。
2、消息类型1) 系统定义消息(System-Defined Messages)在SDK中事先定义好的消息,非用户定义的,其范围在[0x0000, 0x03ff]之间,可以分为以下三类:1> 窗口消息(Windows Message)与窗口的内部运作有关,如创建窗口,绘制窗口,销毁窗口等。
可以是一般的窗口,也可以是Dialog,控件等。
如:WM_CREATE, WM_PAINT, WM_MOUSEMOVE,WM_CTLCOLOR, WM_HSCROLL...2> 命令消息(Command Message)与处理用户请求有关,如单击菜单项或工具栏或控件时,就会产生命令消息。
WM_COMMAND, LOWORD(wParam)表示菜单项,工具栏按钮或控件的ID。
如果是控件, HIWORD(wParam)表示控件消息类型3> 控件通知(Notify Message)控件通知消息,这是最灵活的消息格式,其Message, wParam, lParam分别为:WM_NOTIFY, 控件ID,指向NMHDR的指针。
NMHDR包含控件通知的内容,可以任意扩展。
2) 程序定义消息(Application-Defined Messages)用户自定义的消息,对于其范围有如下规定:WM_USER: 0x0400-0x7FFF (ex. WM_USER+10)WM_APP(winver> 4.0): 0x8000-0xBFFF (ex.WM_APP+4) RegisterWindowMessage: 0xC000-0xFFFF3、消息队列(Message Queues)Windows中有两种类型的消息队列1) 系统消息队列(System Message Queue)这是一个系统唯一的Queue,设备驱动(mouse, keyboard)会把操作输入转化成消息存在系统队列中,然后系统会把此消息放到目标窗口所在的线程的消息队列(thread-specific message queue)中等待处理2) 线程消息队列(Thread-specific Message Queue)每一个GUI线程都会维护这样一个线程消息队列。
(这个队列只有在线程调用GDI函数时才会创建,默认不创建)。
然后线程消息队列中的消息会被送到相应的窗口过程(WndProc)处理.注意:线程消息队列中WM_PAINT,WM_TIMER只有在Queue中没有其他消息的时候才会被处理,WM_PAINT消息还会被合并以提高效率。
其他所有消息以先进先出(FIFO)的方式被处理。
4、队列消息(Queued Messages)和非队列消息(Non-Queued Messages)1)队列消息(Queued Messages)消息会先保存在消息队列中,消息循环会从此队列中取消息并分发到各窗口处理、如鼠标,键盘消息。
2) 非队列消息(NonQueued Messages)消息会绕过系统消息队列和线程消息队列直接发送到窗口过程被处理如:WM_ACTIVATE, WM_SETFOCUS, WM_SETCURSOR,WM_WINDOWPOSCHANGED注意: postMessage发送的消息是队列消息,它会把消息Post到消息队列中;SendMessage发送的消息是非队列消息,被直接送到窗口过程处理.队列消息和非队列消息的区别从消息的发送途径来看,消息可以分成2种:队列消息和非队列消息。
消息队列由可以分成系统消息队列和线程消息队列。
系统消息队列由Windows维护,线程消息队列则由每个GUI线程自己进行维护,为避免给non-GUI现成创建消息队列,所有线程产生时并没有消息队列,仅当线程第一次调用GDI函数时系统才给线程创建一个消息队列。
队列消息送到系统消息队列,然后到线程消息队列;非队列消息直接送给目的窗口过程。
对于队列消息,最常见的是鼠标和键盘触发的消息,例如WM_MOUSERMOVE,WM_CHAR等消息,还有一些其它的消息,例如:WM_PAINT、WM_TIMER和WM_QUIT。
当鼠标、键盘事件被触发后,相应的鼠标或键盘驱动程序就会把这些事件转换成相应的消息,然后输送到系统消息队列,由Windows系统去进行处理。
Windows系统则在适当的时机,从系统消息队列中取出一个消息,根据前面我们所说的MSG消息结构确定消息是要被送往那个窗口,然后把取出的消息送往创建窗口的线程的相应队列,下面的事情就该由线程消息队列操心了,Windows开始忙自己的事情去了。
线程看到自己的消息队列中有消息,就从队列中取出来,通过操作系统发送到合适的窗口过程去处理。
一般来讲,系统总是将消息Post在消息队列的末尾。
这样保证窗口以先进先出的顺序接受消息。
然而,WM_PAINT是一个例外,同一个窗口的多个WM_PAINT被合并成一个WM_PAINT 消息, 合并所有的无效区域到一个无效区域。
合并WM_PAIN的目的是为了减少刷新窗口的次数。
(xys---该图是关闭窗口的整个消息流程,从点击关闭按钮开始)非队列消息将会绕过系统队列和消息队列,直接将消息发送到窗口过程,。
系统发送非队列消息通知窗口,系统发送消息通知窗口。
例如,当用户激活一个窗口系统发送WM_ACTIVATE, WM_SETFOCUS, and WM_SETCURSOR。