众所周知, windows应用程序是消息驱动的,它不是通过显式的系统调用去获取输入,而是等待系统将输入传递给它们。操作系统将所有的输入传递给不同的窗口,每个窗口都会对应一个窗口过程函数,窗口过程函数在窗口获取输入时由操作系统调用,窗口过程函数处理输入并将控制权交还给操作系统。
Windows消息
消息可以由系统产生,也可以由应用程序产生。针对每个输入,系统都会产生一个对应的消息。当应用程序发生改变时,例如改变它的某个窗口的大小时系统也会产生消息。应用程序可以给自己发送消息执行特定任务,也可以通过消息与其它的应用程序通信。
系统传递给窗口过程的消息由四个参数组成:窗口句柄、窗口标识和两个附加参数。
消息类型:系统定义消息和用户定义消息
系统定义消息
系统通过系统定义消息与应用程序通信,它用这些消息来控制应用程序或者将输入提供给应用程序处理。应用程序也可以发送或者提交系统定义消息来控制窗口。每个系统定义消息都会有唯一的一个常量标识符,符号化的常量标识符前缀表明了该消息的种类,不同前缀表示不同类型。系统定义消息范围从0~0x3FF(WM_USER-1)。
应用程序定义消息
应用程序可以定义自己的消息用于自己的窗口或者用于与其它进程的窗口通信。用户定义消息范围应该大于0x3FF(WM_USER)
消息路由方法
系统用两种方法来路由消息:将消息提交给消息队列(队列消息)或者直接将消息发送给窗口过程(非队列消息)。队列消息主要是用户通过鼠标或者键盘提供的输入,例如 WM_MOUSEMOVE, WM_LBUTTONDOWN, WM_KEYDOWN, 和 WM_CHAR 消息,还包括WM_TIMER, WM_PAINT和WM_QUIT,大多数其它消息都属于非队列消息。
队列消息:
系统维护一个唯一的系统消息队列,针对每一个GUI线程,系统也会为该线程维护一个线程相关的消息队列。而非GUI线程则没有消息队列。所有线程在创建的时候都没有消息队列,只有当线程第一次调用GDI函数的时候系统才会为该线程创建消息队列。
当用户移动、点击鼠标或者按下按键时,键盘或鼠标驱动程序将输入成消息并放入系统消息队列中,系统一次从系统消息队列中移除一个消息,检查它的目标窗口,然后将该消息放置在创建目标窗口的线程消息队列中。线程消息队列接收由它创建的所有窗口的鼠标和键盘消息。线程从它的消息队列中移除消息,告诉系统将消息发送到窗口过程函数去处理。
WM_PAINT, WM_TIMER, WM_QUIT消息通常被放置在消息队列的末尾,只有当队列中没有其它消息的时候才去处理它们。并且同一个窗口的多个WM_PAINT消息会被合并成单个WM_PAINT消息,用于减少窗口重绘的次数。
系统通过填充一个MSG结构体并将它拷贝到线程消息队列中来放置消息,MSG结构体包括六个成员。应用程序可以通过调用PostMessage或者PostThreadMessage来往一个线程的消息队列中放置消息。应用程序可以通过调用GetMessage从自己的消息队列中移除消息,然后调用DispatchMessage函数通知系统将消息发送到窗口过程处理。DispatchMessage不会传递消息产生的时间和座标,应用程序可以调用GetMessageTime和GetMessagePos函数获取。
线程可以调用WaitMessage函数在消息队列中没有消息的时候将控制权转交给其它线程,该函数挂起线程并且阻塞直到一个新的消息被放置在该线程的消息队列中。
非队列消息
非队列消息跳过系统消息队列和线程消息队列,直接发送到目标窗口过程。系统通过发送非线程消息来将影响窗口的事件通知给该窗口。例如,当用户激活管委会新的应用程序窗口,系统会发送一系列消息,包括WM_ACTIVATE, WM_SETFOCUS和WM_SETCURSOR。这些消息通知窗口它已经被激活,键盘输入已经被放置在该窗口,鼠标光标已经移动到该窗口边界范围内。当应用程序调用特定的系统函数时也会发送非窗口消息,例如,当应用程序调用SetWindowPos函数来移动窗口的时候系统就会发送WM_WINDOWPOSCHANGED消息。其它系统函数有BroadcastSystemMessage, BroadcastSystemMessageEx, SendMessage, SendMessageTimeout以及SendNotifyMessage。