从内核层说清GetMessage , DispatchMessage
文章目錄
- 要點回顧:
- 為什么拿到句柄非得要回零環?
- 消息隊列(總共有7個小隊列)結構
- GetMessage的聲明:
- GetMessage進入內核:
- GetMessage的功能總結:
- DispatchMessage
- 舉例驗證(有前提情況,仔細觀察)
- ```SendMessage```發送消息運行截圖
- ```PostMessage```發送消息運行截圖
要點回顧:
一個GUI線程有一個消息隊列:
普通線程–>GUI線程–>THREAD.W32THREAD -->THREADINFO–>消息隊列
一個線程可以有多個窗口,所有窗口共享一個消息隊列:
_WINDOW_OBJECT ---->PTHREADINFO pti //所屬線程
---->WNDPROC IpfnWndProc //窗口過程(窗口回調函數)
為什么拿到句柄非得要回零環?
GetMessage(&msg, NULL, 0, 0) TranslateMessage(&msg); DispatchMessage(&msg);這里消息msg的結構體成員如下:
typedef struct tagMSG {HWND hwnd;UINT message;WPARAM wParam;LPARAM lParam;DWORD time;POINT pt;DWORD lPrivate; } MSG, *PMSG, *NPMSG, *LPMSG;這個消息里面存放著窗口的句柄,句柄是什么?句柄它僅僅是一個窗口對象的索引而已,并非當前對象地址,通過句柄找不到相應的窗口回調,它僅僅就是一個窗口對象索引值。窗口回調是存儲在窗口對象里面的,如果需要找到窗口回調,那么我們就需要先找到窗口對象,而窗口對象在哪呢?(窗口與線程的關系)窗口都是由API進入零環去畫出來,一切信息都在零環,這個知識點我們在前面已經說過。。
所以GetMessage,TranslateMessage,DispatchMessage拿著取出來的消息的句柄,進入零環,通過句柄找到相應的窗口對象,通過窗口對象找到相對于的窗口回調函數,然后內核進行調用回調函數。
這也就是為什么非得進入零環的原因。
消息隊列(總共有7個小隊列)結構
1.SentMessagesListHead //接到SendMessage發來的消息
2.PostedMessagesListHead //接到PostMessage發來的消息
3.HardwareMessagesListHead //接到鼠標,鍵盤的消息
…………
…………
根據前面的介紹,消息隊列放在THREADINFO(THREADINFO在KTHREAD結構體中,KTHREAD又在ETHREAD中)中:
USER_MESSAGE_QUEUE又分為七個小隊列
GetMessage的聲明:
GetMessage(LPMSG IpMsg, //返回從隊列中摘下來的消息HWND hWnd, //過濾條件一:(要取的是哪個窗口的消息,如果要專門取哪個窗口的消息,直接把句柄放在此處就行)發個這個窗口的消息UINT wMsgFilterMin, //過濾條件UINT wMsgFilterMax //過濾條件 );GetMessage進入內核:
GetMessage會調用內核層函數w32k!NtUserGetMessage,偽代碼如下:
do{ //先判斷SentMessageListHead do{ …… KeUserModeCallBack(USER32_CALLBACK_WINDOWPROC,Arguments,ArgumentLength,&ResultPointer,&ResultLength); ………… }while(SentMessageListHead!=NULL) }while(其他隊列!=NULL)進入后可以查看到這里有處理SentMessagesListHead 消息隊列的函數:
然后進入后這是從0環回到3環的函數:
GetMessage只處理SendMessage發來的消息,原因可以看上圖,而由PostMessage發來的消息,只是取出,并不會進行近一步處理操作
GetMessage的功能總結:
GetMessage(只處理第一個消息隊列的消息,至于其它消息隊列的消息,GetMessage只負責取出, 然后不管,繼續向下傳遞)的主要功能:
它會首先看SentMessagesListHead 這個隊列,如果有的話,會就地處理
DispatchMessage
DispatchMessage(&msg)//消息的分發,根據窗口句柄調用相關的窗口過程,通過不同的句柄,進入零環找到不同的窗口對象,然后根據窗口對象找到回調函數,并且調用回調函數。
即其他6個消息隊列的處理流程:
User32!DispatchMessage調用w32k!NtUserDispatchMessage
舉例驗證(有前提情況,仔細觀察)
把TranslateMessage(&msg);和DispatchMessage(&msg);注釋掉后,只剩GetMessage(&msg, NULL, 0, 0),然后利用其它程序PostMessage(hwnd, 0x0401, NULL, NULL);和SendMessage(hwnd, 0x0401, NULL, NULL);分別發送消息
前提情況:(特別注意)
SendMessage發送消息運行截圖
SendMessage(hwnd, 0x0401, NULL, NULL);
下圖中我們可以看到
當我們未點擊確定時,發送消息的程序未退出,需要點擊確定后,發送消息的程序收到返回消息,它才會自行退出。這也就是SendMessage的同步問題
當點擊確定后,發送消息程序的運行截圖:
PostMessage發送消息運行截圖
PostMessage(hwnd, 0x0401, NULL, NULL);發送消息的運行截圖:
接收消息的運行截圖:
這里充分說明了上述情況,GetMessage并不會處理PostMessage發送的消息。
注意:
PostMessage發送完消息后,程序即刻退出,并不會等待處理結果,這也就是PostMessage發送消息異步問題
總結
以上是生活随笔為你收集整理的从内核层说清GetMessage , DispatchMessage的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 窗口与线程的关系
- 下一篇: My First Window构造过程,