Windows消息隊列,UI線程,窗口以及消息處理方式總結

轉載地址:http://blog.csdn.net/weiqubo/article/details/7262891

該文章介紹的非常詳細,值得收藏

1.窗口
Windows程序是由一系列的窗口構成的,每個窗口都有自己的窗口過程,窗口過程就是一個擁有有固定 Signature 的 C函數,具體格式如下:

LRESULT CALLBACK WindowProc(HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
);

窗口類型:
可重疊窗口(Overlapped Window),
彈出窗口(Pop-up Window),
子窗口(Child Window)

窗口之間的關係: 父子關係,擁有關係,前後關係。

2.線程
一個進程至少擁有一個線程,稱爲主線程,如果一個線程創建了窗口,擁有GUI資源,那麼也稱該線程爲GUI線程,否則就爲工作線程。窗口是由線程創建的,
創建窗口的線程就擁有該窗口。這種線程擁有關係的概念對窗口有重要的意義:建立窗口的線程必須是爲窗口處理所有消息的線程。爲了使這個概念更加明
確具體,可以想像一個線程建立了一個窗口,然後就結束了。在這種情況下,窗口不會收到一個WM_DESTROY或WM_NCDESTROY消息,因爲線程已經結束,不可能被用來使窗口接收和處理這些消息。每個線程,如果它至少建立了一個窗口,都由系統對它分配一個消息隊列。這個隊列用於窗口消息的派送(dispatch)。爲了使窗口接收這些消息,線程必須有它自己的消息循環,消息循環一般如下:
MSG msg;
while( GetMessage(&msg, NULL, 0, 0) )
{
    TranslateMessage (&msg);
    DispatchMessage (&msg);
}

應用程序不斷的從消息隊列中獲取消息,然後系統通過DispatchMessage函數分派消息到相應窗口的窗口過程,使得消息得到處理。當獲取到WM_QUIT消息時,GetMessage返回0,循環結束。

3.消息
消息,就是指Windows發出的一個通知,告訴應用程序某個事情發生了。例如,單擊鼠標、改變窗口尺寸、按下鍵盤上的一個鍵都會使Windows發送一個消息
給應用程序,它被定義爲:
typedef struct {
    HWND hwnd;    //窗口句柄, 發生在哪個窗口上
    UINT message;   //消息標識號 ( WM_MOUSEMOVE, WM_LBUTTONDOWN, ... )
    WPARAM wParam;   //消息參數1
    LPARAM lParam;   //消息參數2
    DWORD time;
    POINT pt;
} MSG, *PMSG;

一個消息結構體包含了該事件 所有完備信息,當應用程序收到該消息時,就可以做出相應處理了。

消息分類:

<1>.隊列消息和非隊列消息

從消息的發送途徑上看,消息分兩種:隊列消息和非隊列消息。
隊列消息送到系統消息隊列,然後到線程消息隊列;非隊列消息直接送給目的窗口過程。

這裏,對消息隊列闡述如下:
Windows維護一個系統消息隊列(System message queue),每個GUI線程有一個線程消息隊列(Thread message queue)。鼠標、鍵盤事件由鼠標或鍵盤驅動程序轉換成輸入消息並把消息放進系統消息隊列,例如WM_MOUSEMOVE、WM_LBUTTONUP、WM_KEYDOWN、WM_CHAR等等。Windows每次從系統消息隊列移走一個消息,確定它是送給哪個窗口的和這個窗口是由哪個線程創建的,然後,把它放進窗口創建線程的線程消息隊列。線程消息隊列接收送給該線程所創建窗口的消息。線程從消息隊列取出消息,通過Windows把它送給適當的窗口過程來處理。

除了鍵盤、鼠標消息以外,隊列消息還有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 消息處理機制是這樣的 :
首先 Windows OS 把來自硬件 ( 鼠標 , 鍵盤等消息 ) 和來自應用程序的消息放到一個 OS 系統消息隊列中去 ,而應用程序需要有自己的消息隊列 , 也就是線程消息隊列,每一個線程有自己的消息隊列,對於多線程的應用程序就有和線程數目相等的線程消息隊列 .

windows 消息隊列把得到的消息發送到線程消息隊列 , 線程消息隊列每次取出一條消息發送到指定窗口 , 不斷循環直到程序退出 . 這個循環就是靠消息環

while(GetMessage() )
{
    TranslateMessage();
    DispatchMessage();  
}

實現的 .GetMessage() 只是從線程消息中取出一條消息 , 而 DispatchMessage
則把取出的消息發送到目的窗口 . 如果收到 WM_CLOSE 消息則結束循環 , 發送 PostQuitMessage(0), 處理WM_DESTROY 銷燬窗口

Windows 消息隊列 UI 線程,窗口以及消息處理方式總結
1 Windows UI thread:

2 每個窗體是一個 UI thread ,還是隻有一個 UI thread?答: 所有的窗體都用這一個 UI thread

3 Message queue: 每個窗體都有一個 message queue ,還是共用一個 message queue?  答:共用

4 message 處理:是同步還是異步。是每次處理一個消息等這個消息處理完後再處理另一個消息還是每次取一個不等這個消息處理完就處理下一個,也就是 dispatchmessage 什麼時候返回?
答案是要等這個消息處理函數處理完以後才返回,否則會造成消息處理的混亂

5 message 的結構是什麼:包含了窗體的 handle ,消息的類型等
在 Winuser.h 中有定義如下:
typedef struct tagMSG {
HWND        hwnd;
UINT        message;
WPARAM      wParam;
LPARAM      lParam;
DWORD       time;
POINT       pt;
} MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;

其中的成員變量 message 是 WM_SIZE 、 WM_COMMAND 、 WM_QUIT 等等消息標識。
hwnd 中是這個消息所在的窗口句柄。

6 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 來處理,而不會將這個送到消息隊列。

發佈了231 篇原創文章 · 獲贊 5 · 訪問量 20萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章