WIN32说明文件一再强调线程分为GUI线程和work线程两种.GUI线程负责建造窗口以及处理主消息循环,worker负责执行纯粹运算工作,一般而言,GUI线程绝对不会去做那些不能够马上完成大的工作。
GUI线程的定义是:拥有消息队列的线程,任何一个特定窗口的消息总是被产生这一窗口的线程抓到并处理。所有对此窗口的改变也都应该由该线程完成。如果worker线程也产生了一个窗口,那么就会有一个消息队列随之被产生出来并且附着在此线程身上,于是worker线程摇身一变成了GUI线程。这里的意思是说,worker线程不能够产生窗口对话框,消息框,或任何与UI有关的东西。如果一个worker线程需要输入或输出错误信息,它应该授权给UI线程来做,并且将结果通知给worker线程。
说的很彻底,很明了,总结一下,windows处理消息的大致流程应该是这样的:
当我们对GUI有所操作的时候,比方说点了某个按钮或者是某个菜单选项,捕捉到该消息的是windows操作系统,它会根据消息本身的信息构造MSG结构体:
typedef struct tagMSG {
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
#ifdef _MAC
DWORD lPrivate;
#endif
} MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;
系统会根据该消息的信息将其派发到所属线程的消息队列,随后,线程是这样处理这些消息的:
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
GetMessage不断地从自己的消息队列中取消息(线程的消息队列),并且构造MSG结构体, 如果是键盘消息,那么TranslateMessage会对其作一个相当于格式化的操作,DispatchMessage则是根据消息本身的信息(hwnd)将其派发到相应的窗口处理过程。如果消息本身不包含窗口句柄信息,也就是说hwnd为空,那么lParam此时为一个函数指针,此时将直接调用该函数即可(比如WM_TIMER消息)。
线程,消息,GUI之间的关系大致如此,但仍有几个点是黑洞:
1, 线程的消息队列在哪里?它是操作系统为线程创建的,但是我们无法知道它的真面目,只能做些猜测。
2, 系统允许我们设置钩子(HOOK)来截获消息,也就是从操作系统捕获消息到传送到相应线程之间消息会走一个钩子链表(HOOK chain),所谓钩子链表其实就是函数指针的链表,这给程序的安全性带来了很大的威胁,钩子设置的初衷是什么?