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