在Windows應用程序中,窗體是由一種稱爲「UI線程(User Interface Thread)」的特殊類型的線程建立的。函數
首先,UI線程是一種「線程」,因此它具備一個線程應該具備的全部特徵,好比有一個線程函數和一個線程ID。測試
其次,「UI線程」又是「特殊」的,這是由於UI線程的線程函數中會建立一種特殊的對象——窗體,同時,還一併負責建立窗體上的各類控件。spa
每一個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 來處理,而不會將這個送到消息隊列。