Windows消息循环机制详细概述
首先來了解幾個基本概念:
消息:在了解什么是消息先來了解什么是事件。事件可分為幾種,由輸入設備觸發的,比如鼠標鍵盤等等。由窗體控件觸發的,比如button控件,file菜單等。還有就是來自Windows內部的事件。這三種稱為事件。而消息,是由事件翻譯而來的。事件產生消息。
從數據結構角度來講,消息是一種結構體。結構如下:
消息隊列:消息隊列有兩種,分為系統消息隊列和應用程序消息隊列。產生的消息首先由Windows系統捕獲,放在系統消息隊列,再拷貝到對應的應用程序消息隊列。32/64位系統為每一個應用程序維護一個消息隊列。
消息循環:系統為每個應用程序維護一個消息循環,消息循環會不斷檢索自身的消息隊列。每有一個消息,就用GetMessage()取出消息。
GetMessage具有阻塞機制。當消息隊列中沒有消息時,程序非忙等,而是讓權等待。當收到WM_QUIT時,GetMessage返回false,循環停止,同時應用程序終止。
消息處理:DispatchMessage()把取出來的消息分配給相應的窗口或線程,由窗口過程處理函數DefWindowProc()處理。
=========================================================================
以上簡要地介紹了消息隊列,消息循環與消息處理的概念。
Windows的應用程序靠消息驅動來實現功能。而消息驅動靠消息機制來處理。消息機制就是由消息隊列,消息循環,消息處理構成的。
那么,消息機制是如何運作的呢?
當用戶運行一個應用程序,通過對鼠標的點擊或鍵盤按鍵,產生一些特定事件。由于Windows一直監控著I/O設備,該事件首先會被翻譯成消息,由系統捕獲,存放于系統消息隊列。經分析,Windows知道該消息應由那個應用程序處理,則拷貝到相應的應用程序消息隊列。由于消息循環不斷檢索自身的消息隊列,當發現應用程序消息隊列里有消息,就用GetMessage()取出消息,封裝成Msg()結構。如果該消息是由鍵盤按鍵產生的,用TranslateMessage()翻譯為WM_CHAR消息,否則,用DisPatchMessage()將取出的消息分發到相應的應用程序窗口,交由窗口處理程序處理。Windows為每個窗體預留了過程窗口函數,該函數是一個回掉函數,由系統調用,應用程序不能調用。程序員可以通過重載該函數處理我們”感興趣”的消息。對于不感興趣的消息,則由系統默認的窗口過程處理程序做出處理。
下面看這么一張圖:
這張圖很好地解釋了消息機制的運行原理。
當運行程序->事件操作引發消息->消息先存在系統消息隊列->再存入到應用程序消息隊列->用消息循環提取消息->處理消息->再返回消息隊列....
上面代碼的執行過程為:
1. 消息循環調用GetMessage()從消息隊列中查找消息進行處理,如果消息隊列為空,程序將停止執行并等待(程序阻塞)。
2. 事件發生時導致一個消息加入到消息隊列(例如系統注冊了一個鼠標點擊事件),GetMessage()將返回一個正值,這表明有消息需要被處理,并且消息已經填充到傳入的MSG參數中;當傳入WM_QUIT消息時返回0;如果返回值為負表明發生了錯誤。
3. 取出消息(在Msg變量中)并將其傳遞給TranslateMessage()函數,這個函數做一些額外的處理:將虛擬鍵值信息轉換為字符信息。這一步實際上是可選的,但有些地方需要用到這一步。
4. 上面的步驟執行完后,將消息傳遞給DispatchMessage()函數。DispatchMessage()函數將消息分發到消息的目標窗口,并且查找目標窗口過程函數,給窗口過程函數傳遞窗口句柄、消息、wParam、lParam等參數然后調用該函數。
5. 在窗口過程函數中,檢查消息和其他參數,你可以用它來實現你想要的操作。如果不想處理某些特殊的消息,你應該總是調用DefWindowProc()函數,系統將按按默認的方式處理這些消息(通常認為是不做任何操作)。
6. 一旦一個消息處理完成,窗口過程函數返回,DispatchMessage()函數返回,繼續循環處理下一個消息。
/***************************************************************************************************
新建一個win32 Application的默認代碼如下:
程序入口 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//intWINAPI WinMain
定義窗口類 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//typedef struct tagWNDCLASSA
注冊窗口類 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//RegisterClass(&wndclass))
生成窗口 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//CreateWindow
更新窗口 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//ShowWindow(hwnd,iCmdShow);
顯示窗口 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//UpdateWindow(hwnd);
消息循環 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//while(GetMessage(&msg,NULL,0,0))
窗口消息處理(回調函數) ? ? ? ? ? ? ? ? ? ? //LRESULT CALLBACK WndProc
消息循環的代碼如下:
while(GetMessage(&Msg, NULL, 0, 0) > 0)?
{?
? ? TranslateMessage(&Msg); ? ?//轉換
? ? DispatchMessage(&Msg); ? ? //分發
}?
這里有幾個概念,容易混淆:
1.系統:
特指windows操作系統
2.應用程序:
指一個程序,比如QQ,或者酷狗之類的都算一個應用程序
3.窗口:
每個應用程序都 可以擁有窗口,而且可以有多個,但一般會有一個主窗口。例如QQ的主窗口,但是QQ也有很多類似于設置窗口的子窗口,這些窗口都屬于QQ應用程序。
4.消息:
window系統定義了很多種消息,例如,單擊鼠標、改變窗口尺寸、按下鍵盤,這些操作都會使Windows發送一個消息給應用程序。消息本身是作為一個記錄傳遞給應用程序的,這個記錄中包含了消息的類型以及其他信息
5.消息循環:
window系統的一種消息機制
6.消息隊列:
是屬于線程的,是windows系統為線程創建并維護的一個隊列,用于存放各類消息。 系統自身維護一個系統消息隊列,然后還為每個GUI線程線程維護一個線程專門消息隊列。
7.線程:
每個線程默認是沒有消息隊列的,線程只有在第一次調用用戶接口時(比如創建窗口,或者是操作UI元素時),系統才為其創建消息隊列。一個應用程序可以有多個線程,但只能有一個UI線程,默認為主線程,其他子線程是無法操作UI并創建UI元素的。這是windows規定的
windows消息循環的詳細過程:
1.我們創建完win32應用程序,當用戶通過對鼠標,鍵盤操作應用程序時,由于Windows一直監控著I/O設備,該事件首先會被轉化成消息,由windows系統捕獲,存放于系統消息隊列。
2.Windows系統知道該消息應由哪個應用程序處理,然后拷貝到相應的應用程序消息隊列。同時將該消息從系統消息隊列中刪除。
3.應用程序的消息循環不斷在執行,此時,調用GetMessage()從消息隊列中查找消息,發現了該消息,GetMessage()將返回一個正值,并獲取到了該消息Msg;PS:如果消息隊列為空,程序將停止執行并等待(程序阻塞)。
4. 然后取出消息(Msg)并將其傳遞給TranslateMessage()函數,這個函數做一些額外的處理:將虛擬鍵值信息轉換為字符信息。這一步實際上是可選的,但有些地方需要用到這一步。
5. 上面的步驟執行完后,將消息MSG傳遞給DispatchMessage()函數。DispatchMessage()函數將消息再給windows系統,由windows系統找到目標窗口并分發給該窗口,調用消息對應的窗口過程函數,既窗口的WinPro函數,讓WinPro函數處理。WinPro函數可以允許我們對不同的消息做特定的處理,若不處理的話,則會調用DefWindowProc函數,做默認處理,所以為什么默認代碼中WinPro的類型是CallBack(回調),因為不是我們主動調用的,是系統回調的
6. 一旦一個消息處理完成,窗口過程WinPro函數返回,DispatchMessage()函數返回,應用程序的消息循環繼續while循環,Window系統繼續監控各類消息,重復上述步驟
?
總結
以上是生活随笔為你收集整理的Windows消息循环机制详细概述的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: VUE:兄弟组件间传参
- 下一篇: [Git高级教程 (一)] 通过 Ta