日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

windows消息机制深入详解-1

發(fā)布時間:2023/12/18 windows 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 windows消息机制深入详解-1 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Windows 是一個事件驅(qū)動的操作系統(tǒng)。事件驅(qū)動圍繞著消息的產(chǎn)生與處
理展開,事件驅(qū)動是靠消息循環(huán)機制來實現(xiàn)的。也可以理解為消息是一種報告有關(guān)事件發(fā)生
的通知,消息是Windows 操作系統(tǒng)的靈魂。

在屏幕顯示一個窗口一般是以下幾個步驟:

1. 得到應用程序的句柄(GetMouduleHandle,一個程序被加載進內(nèi)存之后,被稱為一個模塊,代表著這個文件中所有的代碼和資源,取這個模塊的句柄方便以后操作這個應用程序)。

2. 注冊窗口類(RegisterClassEx),在注冊之前,要先填寫RegisterClassEX的參數(shù)WNDCLASSEX結(jié)構(gòu)。

3. 建立窗口(CreateWindow)。

4. 顯示窗口(ShowWindow)。

5. 刷新窗口客戶區(qū)(UpdateWindow)。

6. 進入無限的消息循環(huán),如果有消息到達,將消息分派到回調(diào)函數(shù)處理(DispatchMessage),如果是WM_QUIT消息,則退出循環(huán)。回調(diào)函數(shù)是用來處理消息的,也叫窗口過程,這是由windows自己調(diào)用的,我們調(diào)用DispatchMessage函數(shù),而DispatchMessage函數(shù)會自動調(diào)用窗口過程。

while (GetMessage(&Msg, NULL, 0, 0))
{
if (!TranslateAccelerator(hwnd, hAccelerator, &Msg))
???????{
??????????????TranslateMessage(&Msg);
??????????????DispatchMessage(&Msg);
????????}
}

當用戶按下某個鍵、鼠標移動或者拖動窗口等都會產(chǎn)生消息傳給應用程序,應用程序再根據(jù)不同的消息作不同的處理。

Windows的系統(tǒng)內(nèi)部有個系統(tǒng)消息隊列,當用戶當用戶按下某個鍵、鼠標移動或者拖動窗口時,windows都會產(chǎn)生相應的記錄放在消息隊列里。每個記錄中包含著消息的類型、發(fā)生的位置和發(fā)生的時間等。

typedef struct tagMSG {

????HWND ???????hwnd;

????UINT ???????message;

????WPARAM ?????wParam;

????LPARAM ?????lParam;

????DWORD ??????time;

????POINT ??????pt;

#ifdef _MAC

????DWORD ??????lPrivate;

#endif

} MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 窗口程序運行過程


同時,Windows為每個線程維護一個消息隊列,Windows檢查系統(tǒng)消息隊列的發(fā)生位置,當位置位于某個應用程序的窗口范圍內(nèi)時,就把這個消息派送到應用程序的消息隊列里。

當程序中的消息循環(huán)執(zhí)行到GetMessage時,控制權(quán)轉(zhuǎn)移到GetMessage所在的USER32.DLL領(lǐng)空(箭頭1),USER32.DLL從程序消息隊列中取出一條消息(箭頭2),然后把這條消息返回給應用程序。(箭頭3)

應用程序可以對這條消息先作預處理,如可以用TranslateMessage把基于鍵盤掃描碼的按鍵消息轉(zhuǎn)換成基于ASCII的鍵盤消息,如果用到快捷鍵,可以調(diào)用TranslateAccelerator把鍵盤快捷鍵轉(zhuǎn)換成命令消息。

IDM_MAIN?? ?menu?? ?discardable
BEGIN
popup?? ?"關(guān)于(&A)"
BEGIN
menuitem?? ?"關(guān)于我(&A)...\tCtrl+A",?? ?IDC_ABOUT
END
END
//
IDA_MAIN?? ?accelerators ??//加速鍵
BEGIN
"O",?? ?IDC_OPEN,VIRTKEY,CONTROL ??//Ctrl+O
"A",?? ?IDC_ABOUT,VIRTKEY,CONTROL ?//Ctrl+A
END

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 快捷鍵
然后應用程序?qū)⑻幚磉@條消息,但不是自己直接調(diào)用窗口過程來完成,而是通過DispatchMessage間接調(diào)用窗口過程,DispatchMessage 的英文含義是“分派消息”,之所以是“分派”,是因為一個程序可能不只有一個窗口,不同的窗口消息必須分派給相應的窗口過程。當控制權(quán)轉(zhuǎn)移到USER32.DLL領(lǐng)空中的DispatchMessage函數(shù)時,DispatchMessage找出消息對應的窗口過程,然后把消息的具體信息當作參數(shù)來調(diào)用它(箭頭5),窗口過程根據(jù)消息找到對應的分支去處理,然后返回(箭頭6),這時控制權(quán)又回到DispatchMessage,最后DispatchMessage 函數(shù)返回到應用程序領(lǐng)空。(箭頭7)這樣,一個循環(huán)就結(jié)束了,程序又開始新一輪的GetMessage。

? ? ? ? ? ? ? ? ? ? ?窗口程序和對話框程序(分為模態(tài)對話框和非模態(tài)對話框)工作區(qū)別

/*******************************

?Windows是一個消息(Message)驅(qū)動系統(tǒng)。Windows的消息提供了應用程序之間、應用程序與Windows系統(tǒng)之間進行通信的手段。應用程序想要實現(xiàn)的功能由消息來觸發(fā),并且靠對消息的響應和處理來完成。必須注意的是,消息并非是搶占性的,無論事件的緩急,總是按照到達的先后派對,依次處理(一些系統(tǒng)消息除外),這樣可能使一些實時外部事件得不到及時處理。

????? Windows的應用程序一般包含窗口(Window),它主要為用戶提供一種可視化的交互方式,窗口是總是在某個線程(Thread)內(nèi)創(chuàng)建的。Windows系統(tǒng)通過消息機制來管理交互,消息(Message)被發(fā)送,保存,處理,一個線程會維護自己的一套消息隊列(Message Queue),以保持線程間的獨占性。隊列的特點無非是先進先出,這種機制可以實現(xiàn)一種異步的需求響應過程。

目錄:

1、消息

2? 、消息類型

3 、消息隊列(Message Queues)

4 、隊列消息(Queued Messages)和非隊列消息(Non-Queued Messages)

5 、PostMessage(PostThreadMessage), SendMessage

6 、GetMessage, PeekMessage

7 、TranslateMessage, TranslateAccelerator

8、(消息死鎖( Message Deadlocks)

9、BroadcastSystemMessage

10、消息的處理

11、MFC的消息映射

12、消息反射機制

1、消息

??? 消息系統(tǒng)對于一個win32程序來說十分重要,它是一個程序運行的動力源泉。一個消息,是系統(tǒng)定義的一個32位的值,他唯一的定義了一個事件,向Windows發(fā)出一個通知,告訴應用程序某個事情發(fā)生了。例如,單擊鼠標、改變窗口尺寸、按下鍵盤上的一個鍵

都會使Windows發(fā)送一個消息給應用程序。

??? 消息本身是作為一個記錄傳遞給應用程序的,這個記錄中包含了消息的類型以及其他信息。例如,對于單擊鼠標所產(chǎn)生的消息來

說,這個記錄中包含了單擊鼠標時的坐標。這個記錄類型叫做MSG,MSG含有來自windows應用程序消息隊列的消息信息,它在

Windows中聲明如下:?
typedef struct tagMsg?
{?
HWND hwnd;????????? // 接受該消息的窗口句柄?
UINT message;???????? // 消息常量標識符,也就是我們通常所說的消息號?
WPARAM wParam;???? // 32位消息的特定附加信息,確切含義依賴于消息值?
LPARAM lParam;?????? // 32位消息的特定附加信息,確切含義依賴于消息值?
DWORD time;???????? // 消息創(chuàng)建時的時間?
POINT pt;???????????? // 消息創(chuàng)建時的鼠標/光標在屏幕坐標系中的位置?
}MSG;?
? ? 消息可以由系統(tǒng)或者應用程序產(chǎn)生。系統(tǒng)在發(fā)生輸入事件時產(chǎn)生消息。舉個例子, 當用戶敲鍵, 移動鼠標或者單擊控件。系統(tǒng)也產(chǎn)生消息以響應由應用程序帶來的變化, 比如應用程序改變系統(tǒng)字體,改變窗體大小。應用程序可以產(chǎn)生消息使窗體執(zhí)行任務,或者與其他應用程序中的窗口通訊。

2、消息類型

1) 系統(tǒng)定義消息(System-Defined Messages)
? ? ? ? 在SDK中事先定義好的消息,非用戶定義的,其范圍在[0x0000, 0x03ff]之間, 可以分為以下三類:?
1> 窗口消息(Windows Message)
? ? ? ?與窗口的內(nèi)部運作有關(guān),如創(chuàng)建窗口,繪制窗口,銷毀窗口等。可以是一般的窗口,也可以是Dialog,控件等。?
如:WM_CREATE, WM_PAINT, WM_MOUSEMOVE, WM_CTLCOLOR, WM_HSCROLL...?
2> 命令消息(Command Message)
? ? ? ? 與處理用戶請求有關(guān), 如單擊菜單項或工具欄或控件時, 就會產(chǎn)生命令消息。?
WM_COMMAND, LOWORD(wParam)表示菜單項,工具欄按鈕或控件的ID。如果是控件, HIWORD(wParam)表示控件消息類型?
3> 控件通知(Notify Message)
? ? ? ? 控件通知消息, 這是最靈活的消息格式, 其Message, wParam, lParam分別為:WM_NOTIFY, 控件ID,指向NMHDR的指針。NMHDR包含控件通知的內(nèi)容, 可以任意擴展。?
2) 程序定義消息(Application-Defined Messages)
? ? ? ? 用戶自定義的消息, 對于其范圍有如下規(guī)定:?
WM_USER: 0x0400-0x7FFF????? (ex. WM_USER+10)?
WM_APP(winver> 4.0): 0x8000-0xBFFF (ex.WM_APP+4)?
RegisterWindowMessage: 0xC000-0xFFFF

3、消息隊列(Message Queues)

?Windows中有兩種類型的消息隊列?
1) 系統(tǒng)消息隊列(System Message Queue)
? ? ? ? 這是一個系統(tǒng)唯一的Queue,設備驅(qū)動(mouse, keyboard)會把操作輸入轉(zhuǎn)化成消息存在系統(tǒng)隊列中,然后系統(tǒng)會把此消息放到目標窗口所在的線程的消息隊列(thread-specific message queue)中等待處理?
2) 線程消息隊列(Thread-specific Message Queue)
? ? ? ? 每一個GUI線程都會維護這樣一個線程消息隊列。(這個隊列只有在線程調(diào)用GDI函數(shù)時才會創(chuàng)建,默認不創(chuàng)建)。然后線程消息隊列中的消息會被送到相應的窗口過程(WndProc)處理.?
注意: 線程消息隊列中WM_PAINT,WM_TIMER只有在Queue中沒有其他消息的時候才會被處理,WM_PAINT消息還會被合并以提高效率。其他所有消息以先進先出(FIFO)的方式被處理。

4、隊列消息(Queued Messages)和非隊列消息(Non-Queued Messages)

1)隊列消息(Queued Messages)
? ? ? ? 消息會先保存在消息隊列中,消息循環(huán)會從此隊列中取消息并分發(fā)到各窗口處理 ?、如鼠標,鍵盤消息。?
2) 非隊列消息(NonQueued Messages)
? ? ? ? 消息會繞過系統(tǒng)消息隊列和線程消息隊列直接發(fā)送到窗口過程被處理? 如: WM_ACTIVATE, WM_SETFOCUS, WM_SETCURSOR, WM_WINDOWPOSCHANGED?
注意: postMessage發(fā)送的消息是隊列消息,它會把消息Post到消息隊列中; SendMessage發(fā)送的消息是非隊列消息, 被直接送到窗口過程處理.

隊列消息和非隊列消息的區(qū)別
? ? ? ? 從消息的發(fā)送途徑來看,消息可以分成2種:隊列消息和非隊列消息。消息隊列由可以分成系統(tǒng)消息隊列和線程消息隊列。系統(tǒng)消息隊列由Windows維護,線程消息隊列則由每個GUI線程自己進行維護,為避免給non-GUI現(xiàn)成創(chuàng)建消息隊列,所有線程產(chǎn)生時并沒有消息隊列,僅當線程第一次調(diào)用GDI函數(shù)時系統(tǒng)才給線程創(chuàng)建一個消息隊列。隊列消息送到系統(tǒng)消息隊列,然后到線程消息隊列;非隊列消息直接送給目的窗口過程。
???? 對于隊列消息,最常見的是鼠標和鍵盤觸發(fā)的消息,例如WM_MOUSERMOVE,WM_CHAR等消息,還有一些其它的消息,例如:WM_PAINT、 WM_TIMER和WM_QUIT。當鼠標、鍵盤事件被觸發(fā)后,相應的鼠標或鍵盤驅(qū)動程序就會把這些事件轉(zhuǎn)換成相應的消息,然后輸送到系統(tǒng)消息隊列,由 Windows系統(tǒng)去進行處理。Windows系統(tǒng)則在適當?shù)臅r機,從系統(tǒng)消息隊列中取出一個消息,根據(jù)前面我們所說的MSG消息結(jié)構(gòu)確定消息是要被送往那個窗口,然后把取出的消息送往創(chuàng)建窗口的線程的相應隊列,下面的事情就該由線程消息隊列操心了,Windows開始忙自己的事情去了。線程看到自己的消息隊列中有消息,就從隊列中取出來,通過操作系統(tǒng)發(fā)送到合適的窗口過程去處理。
???? 一般來講,系統(tǒng)總是將消息Post在消息隊列的末尾。這樣保證窗口以先進先出的順序接受消息。然而,WM_PAINT是一個例外,同一個窗口的多個 WM_PAINT被合并成一個 WM_PAINT 消息, 合并所有的無效區(qū)域到一個無效區(qū)域。合并WM_PAIN的目的是為了減少刷新窗口的次數(shù)。

? ? ? 非隊列消息將會繞過系統(tǒng)隊列和消息隊列,直接將消息發(fā)送到窗口過程,。系統(tǒng)發(fā)送非隊列消息通知窗口,系統(tǒng)發(fā)送消息通知窗口。例如,當用戶激活一個窗口系統(tǒng)發(fā)送WM_ACTIVATE, WM_SETFOCUS, and WM_SETCURSOR。這些消息通知窗口它被激活了。非隊列消息也可以由當應用程序調(diào)用系統(tǒng)函數(shù)產(chǎn)生。例如,當程序調(diào)用SetWindowPos系統(tǒng)發(fā)送WM_WINDOWPOSCHANGED消息。一些函數(shù)也發(fā)送非隊列消息,例如下面我們要談到的函數(shù)。

5 、PostMessage(PostThreadMessage), SendMessage
? ? ? ? PostMessage:把消息放到指定窗口所在的線程消息隊列中后立即返回。 PostThreadMessage:把消息放到指定線程的消息隊列中后立即返回。?
? ? ? ? SendMessage:直接把消息送到窗口過程處理, 處理完了才返回。

PostMessage(異步)和SendMessage(同步)的區(qū)別

a、 PostMessage 是異步的,SendMessage 是同步的。

? ? ? ? ?PostMessage 只把消息放到隊列,不管消息是不是被處理就返回,消息可能不被處理;

? ? ? ? SendMessage等待消息被處理完了才返回,如果消息不被處理,發(fā)送消息的線程將一直處于阻塞狀態(tài),等待消息的返回。

b、 同一個線程內(nèi):

? ? ? ? ? SendMessage 發(fā)送消息時,由USER32.DLL模塊調(diào)用目標窗口的消息處理程序,并將結(jié)果返回,SendMessage 在同一個線程里面發(fā)送消息不進入線程消息隊列;PostMessage 發(fā)送的消息要先放到消息隊列,然后通過消息循環(huán)分派到目標窗口(DispatchMessage)。

c、不同線程:

???????????? SendMessage 發(fā)送消息到目標窗口的消息隊列,然后發(fā)送消息的線程在USER32。DLL模塊內(nèi)監(jiān)視和等待消息的處理結(jié)果,直到目標窗口的才處理返回,SendMessage在返回之前還需要做許多工作,如響應別的線程向它發(fā)送的SendMessage().PostMessge() 到別的線程的時候最好使用PostThreadMessage?? 代替。PostMessage()的HWND 參數(shù)可以為NULL,相當于PostThreadMessage() + GetCrrentThreadId.

d、系統(tǒng)處理消息。

? ? ? ?系統(tǒng)只處理(marshal)系統(tǒng)消息(0--WM_USER),發(fā)送用戶消息(用戶自己定義)時需要用戶自己處理。

? ? ? ? 使用PostMessage,SendNotifyMessage,SendMessageCallback等異步函數(shù)發(fā)送系統(tǒng)消息時,參數(shù)不可以使用指針,因為發(fā)送者不等待消息的處理就返回,接收者還沒有處理,指針就有可能被釋放了,或則內(nèi)容變化了。

e、在Windows 2000/XP,每個消息隊列最多只能存放一定數(shù)量的消息,超過的將不會被處理就丟掉。系統(tǒng)默認是10000;:[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows] USERPostMessageLimit

6 、GetMessage, PeekMessage
PeekMessage會立即返回??? 可以保留消息?
GetMessage在有消息時返回? 會刪除消息

PeekMessage和GetMessage函數(shù)的主要區(qū)別有:
? ? ? ? a. GetMessage的主要功能是從消息隊列中“取出”消息,消息被取出以后,就從消息隊列中將其刪除;而PeekMessage的主要功能是“窺視”消息,如果有消息,就返回true,否則返回false。也可以使用PeekMessage從消息隊列中取出消息,這要用到它的一個參數(shù)(UINT wRemoveMsg),如果設置為PM_REMOVE,消息則被取出并從消息隊列中刪除;如果設置為PM_NOREMOVE,消息就不會從消息隊列中取出。
? ? ? ? b. 如果GetMessage從消息隊列中取不到消息,則線程就會被操作系統(tǒng)掛起,等到OS重新調(diào)度該線程時,兩者的性質(zhì)不同:使用GetMessage線程仍會被掛起,使用PeekMessage線程會得到CPU的控制權(quán),運行一段時間。
? ? ? ? c、GetMessage每次都會等待消息,直到取到消息才返回;而PeekMessage只是查詢消息隊列,沒有消息就立即返回,從返回值判斷是否取到了消息。
我們也可以說,PeekMessage是一個具有線程異步行為的函數(shù),不管消息隊列中是否有消息,函數(shù)都會立即返回。而GetMessage則是一個具有線程同步行為的函數(shù),如果消息隊列中沒有消息的話,函數(shù)就會一直等待,直到消息隊列中至少有一條消息時才返回。
如果消息隊列中沒有消息,PeekMessage總是能返回,這就相當于在執(zhí)行一個循環(huán),如果消息隊列一直為空, 它就進入了一個死循環(huán)。GetMessage則不可能因為消息隊列為空而進入死循環(huán)。

聯(lián)系:

? ? ? ? 在Windows的內(nèi)部,GetMessage和PeekMessage執(zhí)行著相同的代碼,Peekmessage和Getmessage都是向系統(tǒng)的消息隊列中取得消息,并將其放置在指定的結(jié)構(gòu)。

區(qū)別:

PeekMessage:有消息時返回TRUE,沒有消息返回FALSE?

GetMessage:有消息時且消息不為WM_QUIT時返回TRUE,如果有消息且為WM_QUIT則返回FALSE,沒有消息時不返回。

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從消息隊列中刪除WM_PAINT消息,從隊列中刪除WM_PAINT消息可以令窗口顯示區(qū)域的失效區(qū)域變得有效(刷新窗口),如果隊列中包含WM_PAINT消息程序就會一直while循環(huán)了。

7 、TranslateMessage, TranslateAccelerator
? ? ? ? ?TranslateMessage: 把一個virtual-key消息轉(zhuǎn)化成字符消息(character message),并放到當前線程的消息隊列中,消息循環(huán)下一次取出處理。?
? ? ? ? TranslateAccelerator: 將快捷鍵對應到相應的菜單命令。它會把WM_KEYDOWN 或 WM_SYSKEYDOWN轉(zhuǎn)化成快捷鍵表中相應的WM_COMMAND 或WM_SYSCOMMAND消息, 然后把轉(zhuǎn)化后的 WM_COMMAND或WM_SYSCOMMAND直接發(fā)送到窗口過程處理, 處理完后才會返回。

8、(消息死鎖( Message Deadlocks)
假設有線程A和B, 現(xiàn)在有以下下步驟?
? ? ? ? 1) 線程A SendMessage給線程B, A等待消息在線程B中處理后返回?
? ? ? ? 2) 線程B收到了線程A發(fā)來的消息,并進行處理, 在處理過程中,B也向線程A SendMessgae,然后等待從A返回。? 因為此時, 線程A正等待從線程B返回, 無法處理B發(fā)來的消息, 從而導致了線程A,B相互等待, 形成死鎖。多個線程也可以形成環(huán)形死鎖。?
可以使用 SendNotifyMessage或SendMessageTimeout來避免出現(xiàn)死鎖。

9、BroadcastSystemMessage
? ? ? ? 我們一般所接觸到的消息都是發(fā)送給窗口的, 其實, 消息的接收者可以是多種多樣的,它可以是應用程序(applications), 可安裝驅(qū)動(installable drivers), 網(wǎng)絡設備(network drivers), 系統(tǒng)級設備驅(qū)動(system-level device drivers)等,?
BroadcastSystemMessage這個API可以對以上系統(tǒng)組件發(fā)送消息。

10、消息的處理
? ? ? ? 接下來我們談一下消息的處理,首先我們來看一下VC中的消息泵:

while(GetMessage(&msg, NULL, 0, 0))

{

?????? if(!TranslateAccelerator(msg.hWnd, hAccelTable, &msg))

?{?

??????????? TranslateMessage(&msg);

??????????? DispatchMessage(&msg);

?????? }

}

TranslateMessage(轉(zhuǎn)換消息):

? ? ? ? 用來把虛擬鍵消息轉(zhuǎn)換為字符消息。由于Windows對所有鍵盤編碼都是采用虛擬鍵的定義,這樣當按鍵按下時,并不得字符消息,需要鍵盤映射轉(zhuǎn)換為字符的消息。

TranslateMessage函數(shù)

? ? ? ? 用于將虛擬鍵消息轉(zhuǎn)換為字符消息。字符消息被投遞到調(diào)用線程的消息隊列中,當下一次調(diào)用GetMessage函數(shù)時被取出。當我們敲擊鍵盤上的某個字符鍵時,系統(tǒng)將產(chǎn)生WM_KEYDOWN和WM_KEYUP消息。這兩個消息的附加參數(shù)(wParam和lParam)包含的是虛擬鍵代碼和掃描碼等信息,而我們在程序中往往需要得到某個字符的ASCII碼,TranslateMessage這個函數(shù)就可以將WM_KEYDOWN和WM_ KEYUP消息的組合轉(zhuǎn)換為一條WM_CHAR消息(該消息的wParam附加參數(shù)包含了字符的ASCII碼),并將轉(zhuǎn)換后的新消息投遞到調(diào)用線程的消息隊列中。注意,TranslateMessage函數(shù)并不會修改原有的消息,它只是產(chǎn)生新的消息并投遞到消息隊列中。

也就是說TranslateMessage會發(fā)現(xiàn)消息里是否有字符鍵的消息,如果有字符鍵的消息,就會產(chǎn)生WM_CHAR消息,如果沒有就會產(chǎn)生什么消息。

DispatchMessage(分派消息):

把 TranslateMessage轉(zhuǎn)換的消息發(fā)送到窗口的消息處理函數(shù),此函數(shù)在窗口注冊時已經(jīng)指定。

? ? ? ? 首先,GetMessage從進程的主線程的消息隊列中獲取一個消息并將它復制到MSG結(jié)構(gòu),如果隊列中沒有消息,則GetMessage函數(shù)將等待一個消息的到來以后才返回。如果你將一個窗口句柄作為第二個參數(shù)傳入GetMessage,那么只有指定窗口的的消息可以從隊列中獲得。GetMessage也可以從消息隊列中過濾消息只接受消息隊列中落在范圍內(nèi)的消息。這時候就要利用GetMessage/PeekMessage指定一個消息過濾器。這個過濾器是一個消息標識符的范圍或者是一個窗體句柄,或者兩者同時指定。當應用程序要查找一個后入消息隊列的消息是很有用。WM_KEYFIRST 和 WM_KEYLAST 常量用于接受所有的鍵盤消息。 WM_MOUSEFIRST 和 WM_MOUSELAST 常量用于接受所有的鼠標消息。
然后TranslateAccelerator判斷該消息是不是一個按鍵消息并且是一個加速鍵消息,如果是,則該函數(shù)將把幾個按鍵消息轉(zhuǎn)換成一個加速鍵消息傳遞給窗口的回調(diào)函數(shù)。處理了加速鍵之后,函數(shù)TranslateMessage將把兩個按鍵消息WM_KEYDOWN和WM_KEYUP轉(zhuǎn)換成一個 WM_CHAR,不過需要注意的是,消息WM_KEYDOWN,WM_KEYUP仍然將傳遞給窗口的回調(diào)函數(shù)。????
處理完之后,DispatchMessage函數(shù)將把此消息發(fā)送給該消息指定的窗口中已設定的回調(diào)函數(shù)。如果消息是WM_QUIT,則 GetMessage返回0,從而退出循環(huán)體。應用程序可以使用PostQuitMessage來結(jié)束自己的消息循環(huán)。通常在主窗口的 WM_DESTROY消息中調(diào)用。

11、MFC的消息映射
? ? ? ? ?使用MFC編程時,消息發(fā)送和處理的本質(zhì)和Win32相同,但是,它對消息處理進行了封裝,簡化了程序員編程時消息處理的復雜性,它通過消息映射機制來處理消息,程序員不必去設計和實現(xiàn)自己的窗口過程。
說白了,MFC中的消息映射機制實質(zhì)是一張巨大的消息及其處理函數(shù)對應表。消息映射基本上分為兩大部分:
在頭文件(.h)中有一個宏DECLARE_MESSAGE_MAP(),它放在類的末尾,是一個public屬性的;與之對應的是在實現(xiàn)部分(.cpp)增加了一個消息映射表,內(nèi)容如下:
BEGIN_MASSAGE_MAP(當前類,當前類的基類)
//{{AFX_MSG_MAP(CMainFrame)
消息的入口項
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
但是僅是這兩項還不足以完成一條消息,要是一個消息工作,必須還有以下3個部分去協(xié)作:
1、在類的定義中加入相應的函數(shù)聲明;
2、在類的消息映射表中加入相應的消息映射入口項;
3、在類的實現(xiàn)中加入相應的函數(shù)體;
消息的添加
(1)、利用Class Wizard實現(xiàn)自動添加
? ? ? ? 在菜單中選擇View -> Class Wizard激活Class Wizard,選擇Message Map標簽,從Class name組合框中選取我們想要添加消息的類。在Object IDs列表框中,選取類的名稱。此時,Messages列表框顯示該類的可重載成員函數(shù)和窗口消息。可重載成員函數(shù)顯示在列表的上部,以實際虛構(gòu)成員函數(shù)的大小寫字母來表示。其他為窗口消息,以大寫字母出現(xiàn)。選中我們要添加的消息,單擊Add Funtion按鈕,Class Wizard自動將該消息添加進來。
有時候,我們想要添加的消息在Message列表中找不到,我們可以利用Class Wizard上Class Info標簽以擴展消息列表。在該頁中,找到Message Filter組合框,通過它可以改變首頁中Messages列表框中的選項。
(2)、手動添加消息
?如果Messages列表框中確實沒有我們想要的消息,就需要我們手工添加:
? ? ? ? 1)在類的.h文件中添加處理函數(shù)的聲明,緊接著在//}}AFX_MSG行之后加入聲明,注意,一定要以afx_msg開頭。
通常,添加處理函數(shù)聲明的最好的地方是源代碼中Class Wizard維護的表的下面,在它標記其領(lǐng)域的{{ }}括弧外面。這些括弧中的任何東西都有可能會被Class Wizard銷毀。
? ? ? ? 2)接著,在用戶類的.cpp文件中找到//}}AFX_MSG_MAP行,緊接在它之后加入消息入口項。同樣,也放在{{ }}外面。
? ? ? ? 3)最后,在該文件中添加消息處理函數(shù)的實體。
對于能夠使用Class Wizard添加的消息,盡量使用Class Wizard添加,以減少我們的工作量;對于不能使用Class Wizard添加的消息和自定義消息,需要手動添加。總體說來,MFC的消息編程對用戶來說,相對比較簡單,在此不再使用實例演示。
??????? 12、消息反射機制
什么叫消息反射?
? ? ? ? ?父窗口將控件發(fā)給它的通知消息,反射回控件進行處理(即讓控件處理這個消息),這種通知消息讓控件自己處理的機制叫做消息反射機制。
? ? ? ? 通過前面的學習我們知道,一般情況下,控件向父窗口發(fā)送通知消息,由父窗口處理這些通知消息。這樣,父窗口(通常是一個對話框)會對這些消息進行處理,換句話說,控件的這些消息處理必須在父窗口類體內(nèi),每當我們添加子控件的時候,就要在父窗口類中復制這些代碼。很明顯,這對代碼的維護和移植帶來了不便,而且,明顯背離C++的對象編程原則。
? ? ? ? 從4.0版開始,MFC提供了一種消息反射機制(Message Reflection),可以把控件通知消息反射回控件。具體地講,對于反射消息,如果控件有該消息的處理函數(shù),那么就由控件自己處理該消息,如果控件不處理該消息,則框架會把該消息繼續(xù)送給父窗口,這樣父窗口繼續(xù)處理該消息。可見,新的消息反射機制并不破壞原來的通知消息處理機制。
消息反射機制為控件提供了處理通知消息的機會,這是很有用的。如果按傳統(tǒng)的方法,由父窗口來處理這個消息,則加重了控件對象對父窗口的依賴程度,這顯然違背了面向?qū)ο蟮脑瓌t。若由控件自己處理消息,則使得控件對象具有更大的獨立性,大大方便了代碼的維護和移植。
? ? ? ? 實例M8:簡單地演示MFC的消息反射機制。(見附帶源碼 工程M8)
? ? ?開VC++ 6.0,新建一個基于對話框的工程M8。
在該工程中,新建一個CMyEdit類,基類是CEdit。接著,在該類中添加三個變量,如下:
private:
CBrush m_brBkgnd;
COLORREF m_clrBkgnd;
COLORREF m_clrText;
在CMyEdit::CMyEdit()中,給這三個變量賦初值:
{
m_clrBkgnd = RGB( 255, 255, 0 );
m_clrText = RGB( 0, 0, 0 );
m_brBkgnd.CreateSolidBrush(RGB( 150, 150, 150) );
}
打開ClassWizard,類名為CMyEdit,Messages處選中“=WM_CTLCOLOR”,您是否發(fā)現(xiàn),WM_CTLCOLOR消息前面有一個等號,它表示該消息是反射消息,也就是說,前面有等號的消息是可以反射的消息。
消息反射函數(shù)代碼如下:
HBRUSH CMyEdit::CtlColor(CDC* pDC, UINT nCtlColor)
{
??? // TODO: Change any attributes of the DC here
??? pDC->SetTextColor( m_clrText );//設置文本顏色
??? pDC->SetBkColor( m_clrBkgnd );//設置背景顏色
???? //請注意,在我們改寫該函數(shù)的內(nèi)容前,函數(shù)返回NULL,即return NULL;
??? //函數(shù)返回NULL將會執(zhí)行父窗口的CtlColor函數(shù),而不執(zhí)行控件的CtlColor函數(shù)
??? //所以,我們讓函數(shù)返回背景刷,而不返回NULL,目的就是為了實現(xiàn)消息反射
??? return m_brBkgnd; //返回背景刷
}
在IDD_M8_DIALOG對話框中添加一個Edit控件,使用ClassWizard給該Edit控件添加一個CMyEdit類型的變量m_edit1,把Edit控件和CMyEdit關(guān)聯(lián)起來。

總結(jié)

以上是生活随笔為你收集整理的windows消息机制深入详解-1的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。