基本概念
在windows系统中,线程分为两种,分别是工作线程和UI线程:
工作线程:只处理逻辑的线程,例如:启动一个线程,(线程函数)用来做一个复杂的计算,计算完成之后,此线程就自动退出,这种线程称为工作线程
UI线程:Windows应用程序一般由窗口和控件组成,这也是应用程序与人交互的媒介,拥有窗口(控件)的线程称为UI线程;UI线程可以拥有一个UI线程的消息队列
一个进程可以拥有很多个工作线程,但是只能拥有一个UI线程,即一个进程若拥有UI线程,此UI线程一定是这个进程的主线程
消息与消息队列
- Windows系统与人的交互,是一个
产生消息 -->分发消息 -->处理消息
的过程,例如在某个窗口上点击鼠标,移动鼠标,按下键盘等等用户操作,都会产生一个消息(消息的结构体中会记录下,这个消息是在哪个窗口上产生的(窗口句柄),产生此消息时,鼠标所在位置,等一系列参数);具体的,可以查看Windows系统中,消息结构体的定义:
typedef struct tagMSG {
HWND hwnd; //消息产生的窗口句柄
UINT message; //消息类型
WPARAM wParam; //wparam参数
LPARAM lParam; //lparam参数
DWORD time; //消息产生的时间
POINT pt; //消息产生的坐标
#ifdef _MAC
DWORD lPrivate;
#endif
} MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;
Windows系统内部维护一个系统消息队列,windows会把所有应用程序产生的消息都存储在系统消息队列中
Window进程消息队列: 其实进程是没有消息队列的,消息队列是对线程而言,只不过一个进程中只能有一个UI线程,所以我们习惯性的将这个进程中的UI线程的消息队列,称为进程消息队列
Windows进程消息队列 == Widnow进程中,主(UI)线程的消息队列
- Windows内部还有一个工作线程,这个工作线程,专门从
系统消息队列
中取出消息放到对应的进程消息队列
(根据进程的主(UI)线程有没有包含当前消息的窗口句柄,来判断消息是否属于当前进程)
- 是不是每个进程都具有消息队列呢?
回答是否定的。在Windows下,只有那些具备窗口(GUI用户接口界面)的进程,才会有消息队列,那些不具备GUI的进程是没有消息队列的。也就是说:操作系统在开启一个新的进程时,并没有为其创建消息队列,而是当进程第一次调用GDI函数后,并且进程持有窗口时,才持有消息队列
消息的处理
- 上面介绍了,Windows中有一个工作线程,会从系统消息队列中取出消息,并根据这个消息内的标记(窗口句柄),将这个消息发送给产生此窗口的UI线程,(即把这个消息放到对应的UI线程(应用程序主线程)的消息队列)
- 在UI线程内,一般有一个
消息循环
,类似于以下形式,我们可以看到,消息循环其实是一个while函数,这个函数不断地从此UI线程的消息队列中取出消息,并将消息派发给对应的窗口过程函数
;正是因为有了这个While循环,我们的窗口才可以一直阻塞着不退出,一直与人进行交互 - 那么什么是窗口过程函数呢
实际上,每一个窗口、控件,都有一个属于自己的窗口过程函数(windows默认,或者自定义),用来处理在此窗口、控件上产生的消息;窗口过程函数的基本形式如下:我们可以看到,窗口过程函数,其实就是一个多分支函数罢了,它会根据消息类型的不同,来做不同的处理
最后附图,PostMessage和SendMessage的区别,PostMessage投递到消息队列中,而SendMessage直接投递给窗口过程函数,所以一个是异步(Post),一个是同步(Send)