Windows 应用程序交互过程
应用程序
Windows的应用程序一般包含窗口(Window),它主要为用户提供一种可视化的交互方式(窗口是由线程(Thread)创建的).Windows 系统通过消息机制来让系统和用户进行交互,用户通过触发事件来触发消息,消息(Message)被发送,保存,处理,一个线程会维护自己的一套消息队列(Message Queue)[仅当线程有对应的创建窗口和处理窗口消息时候],在发生输入事件之后,Windows 系统将事件转换为一个消息并将消息放入程序的消息队列中.程序通过执行一块称之为「消息循环」的程序代码从消息队列中取出消息并调用窗体的回调函数处理消息,这样不仅保持线程间的独占性.而且队列的以先进先出方式处理消息来实现异步通信.
但是一般来说不是所有的消息都是需要程序处理的.而是针对其中某些特定的消息进行处理.即程序只关心自己想关心的问题而不会多去处理其他不必要的信息.但是系统并不会知道什么是程序所关心的消息,什么是程序不关心的消息,所以它会把所有的消息都发送给程序.那么程序便要从中塞选出自己想要处理的消息,然后把自己不想理会的消息过滤给系统,让系统去帮助处理这些不需要的消息(即系统提供的默认的窗口过程函数 DefWindowProc).那么一般程序的窗体过程具体流程如下:
上面可以看出窗体可以接收和处理消息的特性.然而控件其实也都是窗口.但是每个控件却不一定都具有接收和处理消息的功能.因为在系统中只有具有句柄(一个系统自动维护的32位的数值,是作为系统对象的简单的唯一性的标识)的窗口才具有处理消息的特性.因为只有具有句柄的窗口才能独立存在作为一窗体存在.作为其他控件的容器.而没有句柄的控件,如Label,是不能独立存在的,只能作为窗口控件的子控件,它不能绘制自身,只能依靠父窗体将它绘制来.
消息
-
消息载体
消息由一个叫MSG的结构体定义,包括窗口句柄(HWND),消息ID(UINT),参数(WPARAM, LPARAM)等等:1 struct MSG 2 { 3 HWND hwnd; 4 UINT message; 5 WPARAM wParam; 6 LPARAM lParam; 7 DWORD time; 8 POINT pt; 9 };
-
消息的简单分类
一般来说消息能够被分为「队列化的」和「非队列化的」.- 队列化的消息是由Windows放入程序消息队列中的.在程序的消息循环中,重新传回并分配给窗口消息处理程序.即把消息发送给程序的消息队列中,等带消息处理函数取出并处理消息.队列化消息基本上是使用者输入的结果,以击键(如WM_KEYDOWN和WM_KEYUP消息)、击键产生的字符(WM_CHAR)、鼠标移动(WM_MOUSEMOVE)和鼠标按钮(WM_LBUTTONDOWN)的形式给出.队列化消息还包含时钟消息(WM_TIMER)、更新消息(WM_PAINT)和退出消息(WM_QUIT).
- 非队列化的消息在Windows呼叫窗口时直接送给窗口消息处理程序.直接进行消息处理.非队列化消息则是其它消息.在许多情况下,非队列化消息来自呼叫特定的Windows函数.例如,当WinMain呼叫CreateWindow时,Windows将建立窗口并在处理中给窗口消息处理程序发送一个WM_CREATE消息.当WinMain呼叫ShowWindow时,Windows将给窗口消息处理程序发送WM_SIZE和WM_SHOWWINDOW消息.当WinMain呼叫UpdateWindow时,Windows将给窗口消息处理程序发送WM_PAINT消息.
- 注意: 键盘或鼠标输入时发出的队列化消息信号,也能在非队列化消息中出现.例如,用键盘或鼠标选择了一个菜单项时,键盘或鼠标消息就是队列化的,而说明菜单项已选中的WM_COMMAND消息则可能就是非队列化的.
- 任何情况下,窗口消息处理程序都将获得窗口所有的消息--包括队列化的和非队列化的
-
消息队列
- 系统队列
当操作系统启动并初始化时,线程Raw Input Thread(RIT)就会启动,并创系统硬件输入队列(System Hardware Input Queue)(SHIQ). 对于外部的硬件事件(鼠标或者键盘),硬件驱动会将事件转换成消息,并存放到SHIQ中,而RIT线程就专门负责处理SHIQ中的消息,把消息分发到对应线程的消息队列里面. - 线程队列
而对于线程来说,每个线程可以拥有自己的消息队列,它和线程一一对应.在线程刚创建时,消息队列并不会被创建,而是当GDI的函数调用发生时,Windows系统才认为有必要为线程创建消息队列.系统会为其维护一个THREADINFO结构.
消息队列包含在一个叫THREADINFO的结构中,有四个队列:- Sent Message Queue 发送消息队列 [该队列保存其他程序通过SendMessage给该线程发送的消息]
- Posted Message Queue 登记消息队列 [该队列保存其他队列通过PostMessage给该线程发送的消息]
- Visualized Input Queue 输入消息队列 [保存系统队列分发过来的消息,比如鼠标或者键盘的消息]
- Reply Message Queue 响应消息队列 [保存向窗体发送消息后的结果,比如sendMessage操作结束后,接收消息方会发送一个Reply消息给发送方的Reply队列中,以唤醒发送队列]
- 系统队列
消息管理机制
-
消息源
消息可以由Windows系统发送,也可以由应用程序本身;可以向线程内发送,也可以跨线程.主要是看发送函数的调用者. -
附加参数:
系统向窗口发送的消息通常包含3个参数,分别是:- 窗口句柄(a window handle):窗口句柄用来标识消息将要发送到的窗口对象,系统使用窗口句柄来确定哪一个窗口句柄应该接收该消息.
- 消息标识符(a message identifier):消息标识符是用来区分不同消息的命名常量,当窗口过程接收到一个消息时,它使用消息标识符来确定如何处理该消息.例如,消息标识符WM_PAINT告诉窗口过程“窗口的客户区已经发生变化,窗口必须进行重新绘制”.
- 消息参数(message parameters):消息参数用来表述窗口过程处理消息时所使用的数据或数据的位置,通常用一对参数表示(即2个长整型的参数:WPARAM, LPARAM).消息参数的意义和取值取决于消息.当不需要使用消息参数时,通常将其设置为NULL.窗口过程必须通过检查消息标识符来确定如何对消息参数进行解释.
-
传递过程:
用户在操作程序时对电脑的操作这个事件将会被系统捕获到,然后把这个事件翻译成一个消息并且把消息投递到对应的消息队列中.而程序发现自己的消息队列中具有消息时它便会从自己的消息队列中取出消息并处理消息.直至消息队列为空为止.
-
三级结构
Windows消息控制中心一般是三层结构,其顶端就是Windows内核.Windows内核维护着一个消息队列,第二级控制中心从这个消息队列中获取属于自己管辖的消息,后做出处理,有些消息直接处理掉,有些还要发送给下一级窗体(Window)或控件(Control).第二级控制中心一般是各Windows应用程序的Application对象.第三级控制中心就是Windows窗体对象,每一个窗体都有一个默认的窗体过程,这个过程负责处理各种接收到的消息.
总结
首先我们要了解 windows 系统下程序是以消息为基础,事件驱动之.首先用户出发事件,然后系统把事件转换为对应的消息,然后把消息投递到消息队列中.应用程序通过消息循环获得消息,然后把消息转交个系统,有系统再把消息交给对应的窗口回调函数(系统通过以注册的窗口类结构中的一个指向回调函数指针的成员找到窗体的回调函数,而这个窗体结构体是在窗体创建过程中设计窗体类时候通过定义一个 WNDCLASS 结构体设计窗体基本属性是定义的),通过窗口回调函数来处理消息,处理消息结束后,应用回调函数把权限在交还给系统然后,系统在执行权限交给应用程序.程序继续从消息队列中取出消息并按上述方法去处理消息之.流程如下: