Windows消息传递机制详解
? ? ? Windows是一個(gè)消息(Message)驅(qū)動(dòng)系統(tǒng)。Windows的消息提供了應(yīng)用程序之間、應(yīng)用程序與Windows系統(tǒng)之間進(jìn)行通信的手段。應(yīng)用程序想要實(shí)現(xiàn)的功能由消息來(lái)觸發(fā),并且靠對(duì)消息的響應(yīng)和處理來(lái)完成。必須注意的是,消息并非是搶占性的,無(wú)論事件的緩急,總是按照到達(dá)的先后派對(duì),依次處理(一些系統(tǒng)消息除外),這樣可能使一些實(shí)時(shí)外部事件得不到及時(shí)處理。
????? Windows的應(yīng)用程序一般包含窗口(Window),它主要為用戶(hù)提供一種可視化的交互方式,窗口是總是在某個(gè)線(xiàn)程(Thread)內(nèi)創(chuàng)建的。Windows系統(tǒng)通過(guò)消息機(jī)制來(lái)管理交互,消息(Message)被發(fā)送,保存,處理,一個(gè)線(xiàn)程會(huì)維護(hù)自己的一套消息隊(duì)列(Message Queue),以保持線(xiàn)程間的獨(dú)占性。隊(duì)列的特點(diǎn)無(wú)非是先進(jìn)先出,這種機(jī)制可以實(shí)現(xiàn)一種異步的需求響應(yīng)過(guò)程。
?
目錄:
1、消息
2? 、消息類(lèi)型
3 、消息隊(duì)列(Message Queues)
4 、隊(duì)列消息(Queued Messages)和非隊(duì)列消息(Non-Queued Messages)
5 、PostMessage(PostThreadMessage), SendMessage
6 、GetMessage, PeekMessage
7 、TranslateMessage, TranslateAccelerator
8、(消息死鎖( Message Deadlocks)
9、BroadcastSystemMessage
10、消息的處理
11、MFC的消息映射
12、消息反射機(jī)制
?
1、消息
??? 消息系統(tǒng)對(duì)于一個(gè)win32程序來(lái)說(shuō)十分重要,它是一個(gè)程序運(yùn)行的動(dòng)力源泉。一個(gè)消息,是系統(tǒng)定義的一個(gè)32位的值,他唯一的定義了一個(gè)事件,向Windows發(fā)出一個(gè)通知,告訴應(yīng)用程序某個(gè)事情發(fā)生了。例如,單擊鼠標(biāo)、改變窗口尺寸、按下鍵盤(pán)上的一個(gè)鍵
都會(huì)使Windows發(fā)送一個(gè)消息給應(yīng)用程序。
??? 消息本身是作為一個(gè)記錄傳遞給應(yīng)用程序的,這個(gè)記錄中包含了消息的類(lèi)型以及其他信息。例如,對(duì)于單擊鼠標(biāo)所產(chǎn)生的消息來(lái)
說(shuō),這個(gè)記錄中包含了單擊鼠標(biāo)時(shí)的坐標(biāo)。這個(gè)記錄類(lèi)型叫做MSG,MSG含有來(lái)自windows應(yīng)用程序消息隊(duì)列的消息信息,它在
Windows中聲明如下:??
typedef struct tagMsg??
{??
HWND hwnd;????????? // 接受該消息的窗口句柄??
UINT message;???????? // 消息常量標(biāo)識(shí)符,也就是我們通常所說(shuō)的消息號(hào)??
WPARAM wParam;???? // 32位消息的特定附加信息,確切含義依賴(lài)于消息值??
LPARAM lParam;?????? // 32位消息的特定附加信息,確切含義依賴(lài)于消息值??
DWORD time;???????? // 消息創(chuàng)建時(shí)的時(shí)間??
POINT pt;???????????? // 消息創(chuàng)建時(shí)的鼠標(biāo)/光標(biāo)在屏幕坐標(biāo)系中的位置??
}MSG;??
? ? 消息可以由系統(tǒng)或者應(yīng)用程序產(chǎn)生。系統(tǒng)在發(fā)生輸入事件時(shí)產(chǎn)生消息。舉個(gè)例子, 當(dāng)用戶(hù)敲鍵, 移動(dòng)鼠標(biāo)或者單擊控件。系統(tǒng)也產(chǎn)生消息以響應(yīng)由應(yīng)用程序帶來(lái)的變化, 比如應(yīng)用程序改變系統(tǒng)字體,改變窗體大小。應(yīng)用程序可以產(chǎn)生消息使窗體執(zhí)行任務(wù),或者與其他應(yīng)用程序中的窗口通訊。
2、消息類(lèi)型
1) 系統(tǒng)定義消息(System-Defined Messages)?
? ? ? ? 在SDK中事先定義好的消息,非用戶(hù)定義的,其范圍在[0x0000, 0x03ff]之間, 可以分為以下三類(lèi):??
1> 窗口消息(Windows Message)?
? ? ? ?與窗口的內(nèi)部運(yùn)作有關(guān),如創(chuàng)建窗口,繪制窗口,銷(xiāo)毀窗口等??梢允且话愕拇翱?#xff0c;也可以是Dialog,控件等。??
如:WM_CREATE, WM_PAINT, WM_MOUSEMOVE, WM_CTLCOLOR, WM_HSCROLL...??
2> 命令消息(Command Message)?
? ? ? ? 與處理用戶(hù)請(qǐng)求有關(guān), 如單擊菜單項(xiàng)或工具欄或控件時(shí), 就會(huì)產(chǎn)生命令消息。??
WM_COMMAND, LOWORD(wParam)表示菜單項(xiàng),工具欄按鈕或控件的ID。如果是控件, HIWORD(wParam)表示控件消息類(lèi)型??
3> 控件通知(Notify Message)?
? ? ? ? 控件通知消息, 這是最靈活的消息格式, 其Message, wParam, lParam分別為:WM_NOTIFY, 控件ID,指向NMHDR的指針。NMHDR包含控件通知的內(nèi)容, 可以任意擴(kuò)展。??
2) 程序定義消息(Application-Defined Messages)?
? ? ? ? 用戶(hù)自定義的消息, 對(duì)于其范圍有如下規(guī)定:??
WM_USER: 0x0400-0x7FFF????? (ex. WM_USER+10)??
WM_APP(winver> 4.0): 0x8000-0xBFFF (ex.WM_APP+4)??
RegisterWindowMessage: 0xC000-0xFFFF
3、消息隊(duì)列(Message Queues)
?Windows中有兩種類(lèi)型的消息隊(duì)列??
1) 系統(tǒng)消息隊(duì)列(System Message Queue)?
? ? ? ? 這是一個(gè)系統(tǒng)唯一的Queue,設(shè)備驅(qū)動(dòng)(mouse, keyboard)會(huì)把操作輸入轉(zhuǎn)化成消息存在系統(tǒng)隊(duì)列中,然后系統(tǒng)會(huì)把此消息放到目標(biāo)窗口所在的線(xiàn)程的消息隊(duì)列(thread-specific message queue)中等待處理??
2) 線(xiàn)程消息隊(duì)列(Thread-specific Message Queue)?
? ? ? ? 每一個(gè)GUI線(xiàn)程都會(huì)維護(hù)這樣一個(gè)線(xiàn)程消息隊(duì)列。(這個(gè)隊(duì)列只有在線(xiàn)程調(diào)用GDI函數(shù)時(shí)才會(huì)創(chuàng)建,默認(rèn)不創(chuàng)建)。然后線(xiàn)程消息隊(duì)列中的消息會(huì)被送到相應(yīng)的窗口過(guò)程(WndProc)處理.??
注意: 線(xiàn)程消息隊(duì)列中WM_PAINT,WM_TIMER只有在Queue中沒(méi)有其他消息的時(shí)候才會(huì)被處理,WM_PAINT消息還會(huì)被合并以提高效率。其他所有消息以先進(jìn)先出(FIFO)的方式被處理。
4、隊(duì)列消息(Queued Messages)和非隊(duì)列消息(Non-Queued Messages)
1)隊(duì)列消息(Queued Messages)?
? ? ? ? 消息會(huì)先保存在消息隊(duì)列中,消息循環(huán)會(huì)從此隊(duì)列中取消息并分發(fā)到各窗口處理 ?、如鼠標(biāo),鍵盤(pán)消息。??
2) 非隊(duì)列消息(NonQueued Messages)?
? ? ? ? 消息會(huì)繞過(guò)系統(tǒng)消息隊(duì)列和線(xiàn)程消息隊(duì)列直接發(fā)送到窗口過(guò)程被處理? 如: WM_ACTIVATE, WM_SETFOCUS, WM_SETCURSOR, WM_WINDOWPOSCHANGED??
注意: postMessage發(fā)送的消息是隊(duì)列消息,它會(huì)把消息Post到消息隊(duì)列中; SendMessage發(fā)送的消息是非隊(duì)列消息, 被直接送到窗口過(guò)程處理.
隊(duì)列消息和非隊(duì)列消息的區(qū)別?
? ? ? ? 從消息的發(fā)送途徑來(lái)看,消息可以分成2種:隊(duì)列消息和非隊(duì)列消息。消息隊(duì)列由可以分成系統(tǒng)消息隊(duì)列和線(xiàn)程消息隊(duì)列。系統(tǒng)消息隊(duì)列由Windows維護(hù),線(xiàn)程消息隊(duì)列則由每個(gè)GUI線(xiàn)程自己進(jìn)行維護(hù),為避免給non-GUI現(xiàn)成創(chuàng)建消息隊(duì)列,所有線(xiàn)程產(chǎn)生時(shí)并沒(méi)有消息隊(duì)列,僅當(dāng)線(xiàn)程第一次調(diào)用GDI函數(shù)時(shí)系統(tǒng)才給線(xiàn)程創(chuàng)建一個(gè)消息隊(duì)列。隊(duì)列消息送到系統(tǒng)消息隊(duì)列,然后到線(xiàn)程消息隊(duì)列;非隊(duì)列消息直接送給目的窗口過(guò)程。?
???? 對(duì)于隊(duì)列消息,最常見(jiàn)的是鼠標(biāo)和鍵盤(pán)觸發(fā)的消息,例如WM_MOUSERMOVE,WM_CHAR等消息,還有一些其它的消息,例如:WM_PAINT、 WM_TIMER和WM_QUIT。當(dāng)鼠標(biāo)、鍵盤(pán)事件被觸發(fā)后,相應(yīng)的鼠標(biāo)或鍵盤(pán)驅(qū)動(dòng)程序就會(huì)把這些事件轉(zhuǎn)換成相應(yīng)的消息,然后輸送到系統(tǒng)消息隊(duì)列,由 Windows系統(tǒng)去進(jìn)行處理。Windows系統(tǒng)則在適當(dāng)?shù)臅r(shí)機(jī),從系統(tǒng)消息隊(duì)列中取出一個(gè)消息,根據(jù)前面我們所說(shuō)的MSG消息結(jié)構(gòu)確定消息是要被送往那個(gè)窗口,然后把取出的消息送往創(chuàng)建窗口的線(xiàn)程的相應(yīng)隊(duì)列,下面的事情就該由線(xiàn)程消息隊(duì)列操心了,Windows開(kāi)始忙自己的事情去了。線(xiàn)程看到自己的消息隊(duì)列中有消息,就從隊(duì)列中取出來(lái),通過(guò)操作系統(tǒng)發(fā)送到合適的窗口過(guò)程去處理。?
???? 一般來(lái)講,系統(tǒng)總是將消息Post在消息隊(duì)列的末尾。這樣保證窗口以先進(jìn)先出的順序接受消息。然而,WM_PAINT是一個(gè)例外,同一個(gè)窗口的多個(gè) WM_PAINT被合并成一個(gè) WM_PAINT 消息, 合并所有的無(wú)效區(qū)域到一個(gè)無(wú)效區(qū)域。合并WM_PAIN的目的是為了減少刷新窗口的次數(shù)。
? ? ? 非隊(duì)列消息將會(huì)繞過(guò)系統(tǒng)隊(duì)列和消息隊(duì)列,直接將消息發(fā)送到窗口過(guò)程,。系統(tǒng)發(fā)送非隊(duì)列消息通知窗口,系統(tǒng)發(fā)送消息通知窗口。例如,當(dāng)用戶(hù)激活一個(gè)窗口系統(tǒng)發(fā)送WM_ACTIVATE, WM_SETFOCUS, and WM_SETCURSOR。這些消息通知窗口它被激活了。非隊(duì)列消息也可以由當(dāng)應(yīng)用程序調(diào)用系統(tǒng)函數(shù)產(chǎn)生。例如,當(dāng)程序調(diào)用SetWindowPos系統(tǒng)發(fā)送WM_WINDOWPOSCHANGED消息。一些函數(shù)也發(fā)送非隊(duì)列消息,例如下面我們要談到的函數(shù)。
5 、PostMessage(PostThreadMessage), SendMessage?
? ? ? ? PostMessage:把消息放到指定窗口所在的線(xiàn)程消息隊(duì)列中后立即返回。 PostThreadMessage:把消息放到指定線(xiàn)程的消息隊(duì)列中后立即返回。??
? ? ? ? SendMessage:直接把消息送到窗口過(guò)程處理, 處理完了才返回。
PostMessage(異步)和SendMessage(同步)的區(qū)別
a、 PostMessage 是異步的,SendMessage 是同步的。
? ? ? ? ?PostMessage 只把消息放到隊(duì)列,不管消息是不是被處理就返回,消息可能不被處理;
? ? ? ? SendMessage等待消息被處理完了才返回,如果消息不被處理,發(fā)送消息的線(xiàn)程將一直處于阻塞狀態(tài),等待消息的返回。
b、 同一個(gè)線(xiàn)程內(nèi):
? ? ? ? ? SendMessage 發(fā)送消息時(shí),由USER32.DLL模塊調(diào)用目標(biāo)窗口的消息處理程序,并將結(jié)果返回,SendMessage 在同一個(gè)線(xiàn)程里面發(fā)送消息不進(jìn)入線(xiàn)程消息隊(duì)列;PostMessage 發(fā)送的消息要先放到消息隊(duì)列,然后通過(guò)消息循環(huán)分派到目標(biāo)窗口(DispatchMessage)。
c、不同線(xiàn)程:
???????????? SendMessage 發(fā)送消息到目標(biāo)窗口的消息隊(duì)列,然后發(fā)送消息的線(xiàn)程在USER32。DLL模塊內(nèi)監(jiān)視和等待消息的處理結(jié)果,直到目標(biāo)窗口的才處理返回,SendMessage在返回之前還需要做許多工作,如響應(yīng)別的線(xiàn)程向它發(fā)送的SendMessage().PostMessge() 到別的線(xiàn)程的時(shí)候最好使用PostThreadMessage?? 代替。PostMessage()的HWND 參數(shù)可以為NULL,相當(dāng)于PostThreadMessage() + GetCrrentThreadId.
d、系統(tǒng)處理消息。
? ? ? ?系統(tǒng)只處理(marshal)系統(tǒng)消息(0--WM_USER),發(fā)送用戶(hù)消息(用戶(hù)自己定義)時(shí)需要用戶(hù)自己處理。
? ? ? ? 使用PostMessage,SendNotifyMessage,SendMessageCallback等異步函數(shù)發(fā)送系統(tǒng)消息時(shí),參數(shù)不可以使用指針,因?yàn)榘l(fā)送者不等待消息的處理就返回,接收者還沒(méi)有處理,指針就有可能被釋放了,或則內(nèi)容變化了。
e、在Windows 2000/XP,每個(gè)消息隊(duì)列最多只能存放一定數(shù)量的消息,超過(guò)的將不會(huì)被處理就丟掉。系統(tǒng)默認(rèn)是10000;:[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows] USERPostMessageLimit
6 、GetMessage, PeekMessage?
PeekMessage會(huì)立即返回??? 可以保留消息??
GetMessage在有消息時(shí)返回? 會(huì)刪除消息
PeekMessage和GetMessage函數(shù)的主要區(qū)別有:?
? ? ? ? a. GetMessage的主要功能是從消息隊(duì)列中“取出”消息,消息被取出以后,就從消息隊(duì)列中將其刪除;而PeekMessage的主要功能是“窺視”消息,如果有消息,就返回true,否則返回false。也可以使用PeekMessage從消息隊(duì)列中取出消息,這要用到它的一個(gè)參數(shù)(UINT wRemoveMsg),如果設(shè)置為PM_REMOVE,消息則被取出并從消息隊(duì)列中刪除;如果設(shè)置為PM_NOREMOVE,消息就不會(huì)從消息隊(duì)列中取出。?
? ? ? ? b. 如果GetMessage從消息隊(duì)列中取不到消息,則線(xiàn)程就會(huì)被操作系統(tǒng)掛起,等到OS重新調(diào)度該線(xiàn)程時(shí),兩者的性質(zhì)不同:使用GetMessage線(xiàn)程仍會(huì)被掛起,使用PeekMessage線(xiàn)程會(huì)得到CPU的控制權(quán),運(yùn)行一段時(shí)間。?
? ? ? ? c、GetMessage每次都會(huì)等待消息,直到取到消息才返回;而PeekMessage只是查詢(xún)消息隊(duì)列,沒(méi)有消息就立即返回,從返回值判斷是否取到了消息。?
我們也可以說(shuō),PeekMessage是一個(gè)具有線(xiàn)程異步行為的函數(shù),不管消息隊(duì)列中是否有消息,函數(shù)都會(huì)立即返回。而GetMessage則是一個(gè)具有線(xiàn)程同步行為的函數(shù),如果消息隊(duì)列中沒(méi)有消息的話(huà),函數(shù)就會(huì)一直等待,直到消息隊(duì)列中至少有一條消息時(shí)才返回。?
如果消息隊(duì)列中沒(méi)有消息,PeekMessage總是能返回,這就相當(dāng)于在執(zhí)行一個(gè)循環(huán),如果消息隊(duì)列一直為空, 它就進(jìn)入了一個(gè)死循環(huán)。GetMessage則不可能因?yàn)橄㈥?duì)列為空而進(jìn)入死循環(huán)。
聯(lián)系:
? ? ? ? 在Windows的內(nèi)部,GetMessage和PeekMessage執(zhí)行著相同的代碼,Peekmessage和Getmessage都是向系統(tǒng)的消息隊(duì)列中取得消息,并將其放置在指定的結(jié)構(gòu)。
區(qū)別:
PeekMessage:有消息時(shí)返回TRUE,沒(méi)有消息返回FALSE?
GetMessage:有消息時(shí)且消息不為WM_QUIT時(shí)返回TRUE,如果有消息且為WM_QUIT則返回FALSE,沒(méi)有消息時(shí)不返回。
GetMessage:取得消息后,刪除除WM_PAINT消息以外的消息。
PeekMessage:取得消息后,根據(jù)wRemoveMsg參數(shù)判斷是否刪除消息。PM_REMOVE則刪除,PM_NOREMOVE不刪除。
The PeekMessage function normally does not remove WM_PAINT messages from the queue. WM_PAINT messages remain in the queue until they are processed. However, if a WM_PAINT message has a null update region, PeekMessage does remove it from the queue.
不能用PeekMessage從消息隊(duì)列中刪除WM_PAINT消息,從隊(duì)列中刪除WM_PAINT消息可以令窗口顯示區(qū)域的失效區(qū)域變得有效(刷新窗口),如果隊(duì)列中包含WM_PAINT消息程序就會(huì)一直while循環(huán)了。
7 、TranslateMessage, TranslateAccelerator?
? ? ? ? ?TranslateMessage: 把一個(gè)virtual-key消息轉(zhuǎn)化成字符消息(character message),并放到當(dāng)前線(xiàn)程的消息隊(duì)列中,消息循環(huán)下一次取出處理。??
? ? ? ? TranslateAccelerator: 將快捷鍵對(duì)應(yīng)到相應(yīng)的菜單命令。它會(huì)把WM_KEYDOWN 或 WM_SYSKEYDOWN轉(zhuǎn)化成快捷鍵表中相應(yīng)的WM_COMMAND 或WM_SYSCOMMAND消息, 然后把轉(zhuǎn)化后的 WM_COMMAND或WM_SYSCOMMAND直接發(fā)送到窗口過(guò)程處理, 處理完后才會(huì)返回。
8、(消息死鎖( Message Deadlocks)?
假設(shè)有線(xiàn)程A和B, 現(xiàn)在有以下下步驟??
? ? ? ? 1) 線(xiàn)程A SendMessage給線(xiàn)程B, A等待消息在線(xiàn)程B中處理后返回??
? ? ? ? 2) 線(xiàn)程B收到了線(xiàn)程A發(fā)來(lái)的消息,并進(jìn)行處理, 在處理過(guò)程中,B也向線(xiàn)程A SendMessgae,然后等待從A返回。? 因?yàn)榇藭r(shí), 線(xiàn)程A正等待從線(xiàn)程B返回, 無(wú)法處理B發(fā)來(lái)的消息, 從而導(dǎo)致了線(xiàn)程A,B相互等待, 形成死鎖。多個(gè)線(xiàn)程也可以形成環(huán)形死鎖。??
可以使用 SendNotifyMessage或SendMessageTimeout來(lái)避免出現(xiàn)死鎖。
9、BroadcastSystemMessage?
? ? ? ? 我們一般所接觸到的消息都是發(fā)送給窗口的, 其實(shí), 消息的接收者可以是多種多樣的,它可以是應(yīng)用程序(applications), 可安裝驅(qū)動(dòng)(installable drivers), 網(wǎng)絡(luò)設(shè)備(network drivers), 系統(tǒng)級(jí)設(shè)備驅(qū)動(dòng)(system-level device drivers)等,??
BroadcastSystemMessage這個(gè)API可以對(duì)以上系統(tǒng)組件發(fā)送消息。
10、消息的處理?
? ? ? ? 接下來(lái)我們談一下消息的處理,首先我們來(lái)看一下VC中的消息泵:
while(GetMessage(&msg, NULL, 0, 0))?
{?
?????? if(!TranslateAccelerator(msg.hWnd, hAccelTable, &msg))?
?{??
??????????? TranslateMessage(&msg);?
??????????? DispatchMessage(&msg);?
?????? }?
}
TranslateMessage(轉(zhuǎn)換消息):
? ? ? ? 用來(lái)把虛擬鍵消息轉(zhuǎn)換為字符消息。由于Windows對(duì)所有鍵盤(pán)編碼都是采用虛擬鍵的定義,這樣當(dāng)按鍵按下時(shí),并不得字符消息,需要鍵盤(pán)映射轉(zhuǎn)換為字符的消息。
TranslateMessage函數(shù)
? ? ? ? 用于將虛擬鍵消息轉(zhuǎn)換為字符消息。字符消息被投遞到調(diào)用線(xiàn)程的消息隊(duì)列中,當(dāng)下一次調(diào)用GetMessage函數(shù)時(shí)被取出。當(dāng)我們敲擊鍵盤(pán)上的某個(gè)字符鍵時(shí),系統(tǒng)將產(chǎn)生WM_KEYDOWN和WM_KEYUP消息。這兩個(gè)消息的附加參數(shù)(wParam和lParam)包含的是虛擬鍵代碼和掃描碼等信息,而我們?cè)诔绦蛑型枰玫侥硞€(gè)字符的ASCII碼,TranslateMessage這個(gè)函數(shù)就可以將WM_KEYDOWN和WM_ KEYUP消息的組合轉(zhuǎn)換為一條WM_CHAR消息(該消息的wParam附加參數(shù)包含了字符的ASCII碼),并將轉(zhuǎn)換后的新消息投遞到調(diào)用線(xiàn)程的消息隊(duì)列中。注意,TranslateMessage函數(shù)并不會(huì)修改原有的消息,它只是產(chǎn)生新的消息并投遞到消息隊(duì)列中。
也就是說(shuō)TranslateMessage會(huì)發(fā)現(xiàn)消息里是否有字符鍵的消息,如果有字符鍵的消息,就會(huì)產(chǎn)生WM_CHAR消息,如果沒(méi)有就會(huì)產(chǎn)生什么消息。
DispatchMessage(分派消息):
把 TranslateMessage轉(zhuǎn)換的消息發(fā)送到窗口的消息處理函數(shù),此函數(shù)在窗口注冊(cè)時(shí)已經(jīng)指定。
? ? ? ? 首先,GetMessage從進(jìn)程的主線(xiàn)程的消息隊(duì)列中獲取一個(gè)消息并將它復(fù)制到MSG結(jié)構(gòu),如果隊(duì)列中沒(méi)有消息,則GetMessage函數(shù)將等待一個(gè)消息的到來(lái)以后才返回。如果你將一個(gè)窗口句柄作為第二個(gè)參數(shù)傳入GetMessage,那么只有指定窗口的的消息可以從隊(duì)列中獲得。GetMessage也可以從消息隊(duì)列中過(guò)濾消息只接受消息隊(duì)列中落在范圍內(nèi)的消息。這時(shí)候就要利用GetMessage/PeekMessage指定一個(gè)消息過(guò)濾器。這個(gè)過(guò)濾器是一個(gè)消息標(biāo)識(shí)符的范圍或者是一個(gè)窗體句柄,或者兩者同時(shí)指定。當(dāng)應(yīng)用程序要查找一個(gè)后入消息隊(duì)列的消息是很有用。WM_KEYFIRST 和 WM_KEYLAST 常量用于接受所有的鍵盤(pán)消息。 WM_MOUSEFIRST 和 WM_MOUSELAST 常量用于接受所有的鼠標(biāo)消息。?
然后TranslateAccelerator判斷該消息是不是一個(gè)按鍵消息并且是一個(gè)加速鍵消息,如果是,則該函數(shù)將把幾個(gè)按鍵消息轉(zhuǎn)換成一個(gè)加速鍵消息傳遞給窗口的回調(diào)函數(shù)。處理了加速鍵之后,函數(shù)TranslateMessage將把兩個(gè)按鍵消息WM_KEYDOWN和WM_KEYUP轉(zhuǎn)換成一個(gè) WM_CHAR,不過(guò)需要注意的是,消息WM_KEYDOWN,WM_KEYUP仍然將傳遞給窗口的回調(diào)函數(shù)。?????
處理完之后,DispatchMessage函數(shù)將把此消息發(fā)送給該消息指定的窗口中已設(shè)定的回調(diào)函數(shù)。如果消息是WM_QUIT,則 GetMessage返回0,從而退出循環(huán)體。應(yīng)用程序可以使用PostQuitMessage來(lái)結(jié)束自己的消息循環(huán)。通常在主窗口的 WM_DESTROY消息中調(diào)用。
11、MFC的消息映射?
? ? ? ? ?使用MFC編程時(shí),消息發(fā)送和處理的本質(zhì)和Win32相同,但是,它對(duì)消息處理進(jìn)行了封裝,簡(jiǎn)化了程序員編程時(shí)消息處理的復(fù)雜性,它通過(guò)消息映射機(jī)制來(lái)處理消息,程序員不必去設(shè)計(jì)和實(shí)現(xiàn)自己的窗口過(guò)程。?
說(shuō)白了,MFC中的消息映射機(jī)制實(shí)質(zhì)是一張巨大的消息及其處理函數(shù)對(duì)應(yīng)表。消息映射基本上分為兩大部分:?
在頭文件(.h)中有一個(gè)宏DECLARE_MESSAGE_MAP(),它放在類(lèi)的末尾,是一個(gè)public屬性的;與之對(duì)應(yīng)的是在實(shí)現(xiàn)部分(.cpp)增加了一個(gè)消息映射表,內(nèi)容如下:?
BEGIN_MASSAGE_MAP(當(dāng)前類(lèi),當(dāng)前類(lèi)的基類(lèi))?
//{{AFX_MSG_MAP(CMainFrame)?
消息的入口項(xiàng)?
//}}AFX_MSG_MAP?
END_MESSAGE_MAP()?
但是僅是這兩項(xiàng)還不足以完成一條消息,要是一個(gè)消息工作,必須還有以下3個(gè)部分去協(xié)作:?
1、在類(lèi)的定義中加入相應(yīng)的函數(shù)聲明;?
2、在類(lèi)的消息映射表中加入相應(yīng)的消息映射入口項(xiàng);?
3、在類(lèi)的實(shí)現(xiàn)中加入相應(yīng)的函數(shù)體;?
消息的添加?
(1)、利用Class Wizard實(shí)現(xiàn)自動(dòng)添加?
? ? ? ? 在菜單中選擇View -> Class Wizard激活Class Wizard,選擇Message Map標(biāo)簽,從Class name組合框中選取我們想要添加消息的類(lèi)。在Object IDs列表框中,選取類(lèi)的名稱(chēng)。此時(shí),Messages列表框顯示該類(lèi)的可重載成員函數(shù)和窗口消息??芍剌d成員函數(shù)顯示在列表的上部,以實(shí)際虛構(gòu)成員函數(shù)的大小寫(xiě)字母來(lái)表示。其他為窗口消息,以大寫(xiě)字母出現(xiàn)。選中我們要添加的消息,單擊Add Funtion按鈕,Class Wizard自動(dòng)將該消息添加進(jìn)來(lái)。?
有時(shí)候,我們想要添加的消息在Message列表中找不到,我們可以利用Class Wizard上Class Info標(biāo)簽以擴(kuò)展消息列表。在該頁(yè)中,找到Message Filter組合框,通過(guò)它可以改變首頁(yè)中Messages列表框中的選項(xiàng)。?
(2)、手動(dòng)添加消息?
?如果Messages列表框中確實(shí)沒(méi)有我們想要的消息,就需要我們手工添加:?
? ? ? ? 1)在類(lèi)的.h文件中添加處理函數(shù)的聲明,緊接著在//}}AFX_MSG行之后加入聲明,注意,一定要以afx_msg開(kāi)頭。?
通常,添加處理函數(shù)聲明的最好的地方是源代碼中Class Wizard維護(hù)的表的下面,在它標(biāo)記其領(lǐng)域的{{ }}括弧外面。這些括弧中的任何東西都有可能會(huì)被Class Wizard銷(xiāo)毀。?
? ? ? ? 2)接著,在用戶(hù)類(lèi)的.cpp文件中找到//}}AFX_MSG_MAP行,緊接在它之后加入消息入口項(xiàng)。同樣,也放在{{ }}外面。?
? ? ? ? 3)最后,在該文件中添加消息處理函數(shù)的實(shí)體。?
對(duì)于能夠使用Class Wizard添加的消息,盡量使用Class Wizard添加,以減少我們的工作量;對(duì)于不能使用Class Wizard添加的消息和自定義消息,需要手動(dòng)添加。總體說(shuō)來(lái),MFC的消息編程對(duì)用戶(hù)來(lái)說(shuō),相對(duì)比較簡(jiǎn)單,在此不再使用實(shí)例演示。?
??????? 12、消息反射機(jī)制?
什么叫消息反射??
? ? ? ? ?父窗口將控件發(fā)給它的通知消息,反射回控件進(jìn)行處理(即讓控件處理這個(gè)消息),這種通知消息讓控件自己處理的機(jī)制叫做消息反射機(jī)制。?
? ? ? ? 通過(guò)前面的學(xué)習(xí)我們知道,一般情況下,控件向父窗口發(fā)送通知消息,由父窗口處理這些通知消息。這樣,父窗口(通常是一個(gè)對(duì)話(huà)框)會(huì)對(duì)這些消息進(jìn)行處理,換句話(huà)說(shuō),控件的這些消息處理必須在父窗口類(lèi)體內(nèi),每當(dāng)我們添加子控件的時(shí)候,就要在父窗口類(lèi)中復(fù)制這些代碼。很明顯,這對(duì)代碼的維護(hù)和移植帶來(lái)了不便,而且,明顯背離C++的對(duì)象編程原則。?
? ? ? ? 從4.0版開(kāi)始,MFC提供了一種消息反射機(jī)制(Message Reflection),可以把控件通知消息反射回控件。具體地講,對(duì)于反射消息,如果控件有該消息的處理函數(shù),那么就由控件自己處理該消息,如果控件不處理該消息,則框架會(huì)把該消息繼續(xù)送給父窗口,這樣父窗口繼續(xù)處理該消息。可見(jiàn),新的消息反射機(jī)制并不破壞原來(lái)的通知消息處理機(jī)制。?
消息反射機(jī)制為控件提供了處理通知消息的機(jī)會(huì),這是很有用的。如果按傳統(tǒng)的方法,由父窗口來(lái)處理這個(gè)消息,則加重了控件對(duì)象對(duì)父窗口的依賴(lài)程度,這顯然違背了面向?qū)ο蟮脑瓌t。若由控件自己處理消息,則使得控件對(duì)象具有更大的獨(dú)立性,大大方便了代碼的維護(hù)和移植。?
? ? ? ? 實(shí)例M8:簡(jiǎn)單地演示MFC的消息反射機(jī)制。(見(jiàn)附帶源碼 工程M8)?
? ? ?開(kāi)VC++ 6.0,新建一個(gè)基于對(duì)話(huà)框的工程M8。?
在該工程中,新建一個(gè)CMyEdit類(lèi),基類(lèi)是CEdit。接著,在該類(lèi)中添加三個(gè)變量,如下:?
private:?
CBrush m_brBkgnd;?
COLORREF m_clrBkgnd;?
COLORREF m_clrText;?
在CMyEdit::CMyEdit()中,給這三個(gè)變量賦初值:?
{?
m_clrBkgnd = RGB( 255, 255, 0 );?
m_clrText = RGB( 0, 0, 0 );?
m_brBkgnd.CreateSolidBrush(RGB( 150, 150, 150) );?
}?
打開(kāi)ClassWizard,類(lèi)名為CMyEdit,Messages處選中“=WM_CTLCOLOR”,您是否發(fā)現(xiàn),WM_CTLCOLOR消息前面有一個(gè)等號(hào),它表示該消息是反射消息,也就是說(shuō),前面有等號(hào)的消息是可以反射的消息。?
消息反射函數(shù)代碼如下:?
HBRUSH CMyEdit::CtlColor(CDC* pDC, UINT nCtlColor)?
{?
??? // TODO: Change any attributes of the DC here?
??? pDC->SetTextColor( m_clrText );//設(shè)置文本顏色?
??? pDC->SetBkColor( m_clrBkgnd );//設(shè)置背景顏色?
???? //請(qǐng)注意,在我們改寫(xiě)該函數(shù)的內(nèi)容前,函數(shù)返回NULL,即return NULL;?
??? //函數(shù)返回NULL將會(huì)執(zhí)行父窗口的CtlColor函數(shù),而不執(zhí)行控件的CtlColor函數(shù)?
??? //所以,我們讓函數(shù)返回背景刷,而不返回NULL,目的就是為了實(shí)現(xiàn)消息反射?
??? return m_brBkgnd; //返回背景刷?
}?
在IDD_M8_DIALOG對(duì)話(huà)框中添加一個(gè)Edit控件,使用ClassWizard給該Edit控件添加一個(gè)CMyEdit類(lèi)型的變量m_edit1,把Edit控件和CMyEdit關(guān)聯(lián)起來(lái)。
林炳文Evankaka原創(chuàng)作品。轉(zhuǎn)載請(qǐng)注明出處http://blog.csdn.net/evankaka
轉(zhuǎn)載于:https://www.cnblogs.com/icooper/p/4574916.html
總結(jié)
以上是生活随笔為你收集整理的Windows消息传递机制详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: EF – 8.多对多关联
- 下一篇: [转载] KAFKA分布式消息系统