在Windows应用程序中,窗体是由一种称为“UI线程(User Interface Thread)”的特殊类型的线程创建的。
首先,UI线程是一种“线程”,所以它具有一个线程应该具有的所有特征,比如有一个线程函数和一个线程ID。
其次,“UI线程”又是“特殊”的,这是因为UI线程的线程函数中会创建一种特殊的对象——窗体,同时,还一并负责创建窗体上的各种控件。
每个UI线程都有一个消息队列,而不是每个窗体一个消息队列!
只有当一个线程调用Win32 API中的GDI(Graphics Device Interface)和User函数时,操作系统才会将其看成是一个UI线程,并为它创建一个消息队列。
需要注意的是,消息循环是由UI线程的线程函数启动的(如果函数线程 没有实现消息循环函数就不能处理消息,这个概念要牢记。只产生了一个窗口,但是没有消息处理函数,窗口就无法处理消息),操作系统不管这件事,它只管为UI线程创建消息队列。因此,如果某个UI线程的线程函数中没有定义消息循环,那么,它所拥有的窗体是无法正确绘制的。
如果一个线程创建了窗口,拥有GUI资源,那么也称该线程为GUI线程,否则就为工作线程。创建窗口的线程就拥有该窗口。这种线程拥有关系的概念对窗口有重要的意义:建立窗口的线程必须是为窗口处理所有消息的线程。为了使这个概念更加明确具体,可以想像一个线程建立了一个窗口,然后就结束了。在这种情况下,窗口不会收到一个WM_DESTROY或WM_NCDESTROY消息,因为线程已经结束,不可能被用来使窗口接收和处理这些消息。每个线程,如果它至少建立了一个窗口,都由系统对它分配一个消息队列。这个队列用于窗口消息的派送(dispatch)。为了使窗口接收这些消息,线程必须有它自己的消息循环。
消息分类:
<1>.队列消息和非队列消息
从消息的发送途径上看,消息分两种:队列消息和非队列消息。
队列消息送到系统消息队列,然后到线程消息队列;非队列消息直接送给目的窗口过程。
这里,对消息队列阐述如下:
Windows维护一个系统消息队列(System message queue),每个GUI线程有一个线程消息队列(Thread message queue)。
一般键盘、鼠标消息是队列消息,此外队列消息还有WM_PAINT、WM_TIMER和WM_QUIT。这些队列消息以外的绝大多数消息是非队列消息。
<2>.系统消息和应用程序消息
系统消息ID的范围是从0到WM_USER-1,或0X80000到0XBFFFF;
应用程序消息从WM_USER(0X0400)到0X7FFF,或0XC000到0XFFFF;WM_USER到0X7FFF范围的消息由应用程序自己使用;0XC000到0XFFFF范围的消息用来和其他应用程序通信,为了ID的唯一性,使用::RegisterWindowMessage来得到该范围的消息ID。
<3>.窗口消息,命令消息,控件通知消息
根据处理过程的不同,可以分为三类:窗口消息,命令消息,控件通知消息。
(1).窗口消息
一般以WM_开头,如WM_CREATE, WM_SIZE, WM_MOUSEMOVE等标准的Windows消息, 用于窗口相关的事件通知,窗口消息将由系统分配到该窗口的窗口过程处理。
(2).命令消息 (WM_COMMAND)
一种特殊的窗口消息,它从一个窗口发送到另一个窗口以处理来自用户的请求,通常是从子窗口发送到父窗口,例如,点击按钮时,按钮的父窗口会收到
WM_COMMAND消息,用以通知父窗口按钮被点击,经测试:子窗口向父窗口发送WM_COMMAND消息,或者称为父窗口会收到WM_COMMAND消息,操作系统并不是通过将WM_COMMAND消息放入到父窗口的消息队列中去,而是直接调用了父窗口的窗口过程,以 WM_COMMAND 为消息标识参数(UINT uMsg),实现这个功能的API函数正是: LRESULT DispatchMessage(const MSG *lpmsg);
(3).控件通知消息
WM_NOTIFY消息,当用户与控件交互(Edit, Button...)时,通知消息会从控件窗口发送到父窗口,这种消息的目的不是为了处理用户命令,而是为了让父窗
口能够适时的改变控件。
Windows 如何知道消息应该送到哪一个线程?
这里我们要分为两种情况 , 消息是不是队列消息 。
是队列消息:
比如在一个窗体空白处点击左键 , 首先 OS 会根据当前的context 来生成 MSG , MSG 中会包括要发送到的窗口的 Handle。首先 OS 会将这个消息放到 OS 的系统消息队列中 , 而后 OS 会有专门的进程根据 MSG 中的窗口的 Handle 找到创建该窗口的线程,而后将该 MSG 送到该线程的消息队列,而后由该消息循环来处理这个消息, 最终由DispatchMessage 函数来将这个消息送到相应的窗口处理函数。
非队列消息:
如果你在一个窗体上点击了一个 button 呢,消息的路径是怎样的呢?当你点击了一个 button 后, OS 产生三个MSG。 WM_LBUTTONDOWN和WM_LBUTTONUP,这两个消息的窗口Handle为button的handle。一个WM_command 或者 wm_notify 消息, OS 会将这个消息直接送给包含 button 的 window processdure 来处理,而不会将这个送到消息队列。
来源:oschina
链接:https://my.oschina.net/u/2453047/blog/717875