總的來說:
MSG包括:
窗口句柄,指示MSG發(fā)送的目的窗口
消息標(biāo)識(shí)
lPARAM、wParam
發(fā)送時(shí)間
發(fā)送時(shí)的鼠標(biāo)位置
關(guān)于消息隊(duì)列:
Windows系統(tǒng)有一個(gè)系統(tǒng)消息隊(duì)列
每個(gè)線程都有一個(gè)自己的消 息隊(duì)列(由于發(fā)送消息MSG需 要提供一個(gè)窗口HWnd,而基 本有窗口的線程,都是UI線 程),因此基本上如果線程使用了GDI函數(shù),則windows給該線程分配一個(gè)線程消息隊(duì)列,這個(gè)消息隊(duì)列負(fù)責(zé)該線程的所有窗口的消息。
所有的窗口都有自己的句柄(HWND),消息被發(fā)送時(shí),這個(gè)句柄就已經(jīng)被指定了。所以 當(dāng)子窗口收到一個(gè)消息時(shí),其父窗口不會(huì)也收到這個(gè)消息,除非子窗口手 動(dòng)的轉(zhuǎn)發(fā)。
消息分為:
實(shí)際上MSDN把消息分為隊(duì)列型(Queued Message)和非隊(duì)列型(Non-queued Message),這只是不同的路由方式, 但最終都會(huì)由消息處理函數(shù)來處理。
隊(duì) 列型消息包括硬件的輸入(WM_KEY*等)、WM_TIMER消 息、WM_PAINT消息等;非隊(duì)列型的一些例子有WM_SETFOCUS, WM_ACTIVE, WM_SETCURSOR等,它們被直接發(fā)送給處理函數(shù)。
其實(shí),按照MSDN的說法和消息的路由過程可以理 解為,Posted Message Queue里的消息是真正的隊(duì)列型消 息,而通過SendMessage()發(fā)送到消息,即使它進(jìn)入了Sent Message Queue,由于SendMessage要求的同步處理,這些消息也 應(yīng)該算非隊(duì)列型消息。也許,Windows系統(tǒng)會(huì)特殊處理,使消息強(qiáng)行繞 過隊(duì)列。
=====================
一節(jié)詳細(xì)描述消息和消息隊(duì)列以及如何在 你程序中使用他們。
關(guān)于消息和消息 隊(duì)列
與傳統(tǒng)的應(yīng)用程序不 同,Microsoft Windows應(yīng)用程序并不顯式地用一個(gè)函數(shù)的調(diào)用(如c運(yùn)行庫(kù))來獲取輸入,而是,等待windows系統(tǒng)把輸入傳給它們。
windows系統(tǒng)把應(yīng)用程序 的所有輸入傳給應(yīng)用程序的窗口,每個(gè)窗口都有一個(gè)稱之為窗口過程的函數(shù).當(dāng)窗口有輸入時(shí)windows系統(tǒng)要調(diào)用它,窗口過程處理輸入并把控制返回 windows系統(tǒng)。有關(guān)窗口過程,參見 “窗口過程”。 這一章講述消息及消息隊(duì)列,并說明在應(yīng)用程序中如何使用它們。
消息
windows系統(tǒng)以消息的形式把輸入傳給窗口過程,消息是由windows系 統(tǒng)或應(yīng)用程序產(chǎn)生的.windows系統(tǒng)對(duì)每一個(gè)輸入事件都要產(chǎn)生消息,例如,用戶按鍵盤、移動(dòng)鼠標(biāo)或單擊一個(gè)滾動(dòng)條控制框。windows系統(tǒng)為了響應(yīng) 應(yīng)用程序給系統(tǒng)帶來的變化也會(huì)產(chǎn)生消息,比如應(yīng)用程序改變了系統(tǒng)字體資源池或是改變了一個(gè)窗門的大小。應(yīng)用程序可通過產(chǎn)生消息指導(dǎo)它自己的窗口來完成某個(gè) 任務(wù),或是與其它應(yīng)用程序的窗口進(jìn)行通信。
windows 系統(tǒng)把消息發(fā)送給窗口過程.窗口過程有四個(gè)參數(shù):窗口句柄、消息標(biāo)識(shí)以及兩個(gè)叫做消息參數(shù)的32位值。窗口句柄決定消息將發(fā)送到哪—個(gè)窗 口,windows系統(tǒng)則用它來確定向哪一個(gè)窗口過程發(fā)送消息。
消息標(biāo)識(shí)是一個(gè)命名的常量,由它來標(biāo)明消息的目的。如果窗口過程接收到一條消息,它就通過消息標(biāo)識(shí)來決定如何處理這條 消息。例如,消息標(biāo)識(shí)WM_PAINT 通知窗口過程,窗口的客戶區(qū)被改變了,需要重畫。
消息參數(shù)指定窗口過程在處理消息時(shí)所用的數(shù)據(jù)或數(shù)據(jù)的位 置,消息的意圖及數(shù)值取決了消息本身。消息參數(shù)可以是一個(gè)整數(shù)、緊縮的位標(biāo)志、一個(gè)含有附加數(shù)據(jù)結(jié)構(gòu)的指針等等。如果消息不使用消息參數(shù),一般就都設(shè)置成NULL 、 窗口過程必須檢查消息標(biāo)識(shí)以確定如何解釋消息參數(shù)。
消息路由
windows系統(tǒng)用兩種方式向窗口過程發(fā)送消息:把消息投遞到一個(gè)先進(jìn)先出的消息隊(duì)列中,它是一個(gè)系統(tǒng)定義的內(nèi)存塊用于臨時(shí)存儲(chǔ)消息;或是把消 息直接發(fā)給窗口過程。
投遞到 消息隊(duì)列中的消息叫排隊(duì)消息,它們主要是用戶通過鼠標(biāo)或鍵盤的輸入結(jié)果.如WM_MOUSEMOVE, WM_LBUTTONDOWN, WM_KEYDOWN, and WM_CHAR 消息。其它的排隊(duì)消息包括定時(shí)器、繪制和退出消息:WM_TIMER, WM_PAINT, and WM_QUIT 。所有直接發(fā)送到窗口過程的其它消息稱之為非排隊(duì)消息。
排隊(duì)消息
windows系統(tǒng)在同一時(shí)間可顯示多個(gè)窗口,要發(fā)送鼠標(biāo)和鍵盤輸入到相應(yīng) 的窗口,windows系統(tǒng)要用到消息隊(duì)列,它要管理一個(gè)系統(tǒng)消息隊(duì)列和任意數(shù)目線程消息隊(duì)列,每一個(gè)隊(duì)列對(duì)應(yīng)于一個(gè)線程。
不管什么時(shí)候,只要用戶移動(dòng)鼠標(biāo)或是敲鍵盤.鼠標(biāo)或鍵盤的 設(shè)備驅(qū)動(dòng)器都要把輸入轉(zhuǎn)換成消息,并把它們放到系統(tǒng)消息隊(duì)列中去。windows從系統(tǒng)隊(duì)列中每次移走一條消息,確定目的窗口,再把它們投遞到創(chuàng)建目的窗 口的線程的消息隊(duì)列中,線程消息隊(duì)列接收所有由該線程創(chuàng)建的窗口的鼠標(biāo)和鍵盤消息。線程從它的隊(duì)列中移走消息并指導(dǎo)windows系統(tǒng)將它們發(fā)送到相應(yīng)的 窗口過程進(jìn)行處理。有關(guān)線程,參見 “進(jìn)程和線程”。
WM_PAINT 消息有點(diǎn)特別,windows系統(tǒng)總是把這條消息放在消息隊(duì)列的最后,這樣 可保證窗口按先進(jìn)先出次序接收它的輸入消息,WM_PAINT 消息被保持在隊(duì)列中,只有在隊(duì)列中沒有其它消息時(shí)才發(fā)送 到窗口過程。同一個(gè)窗口的多個(gè)WM_PAINT 消息被合并成一個(gè)WM_PAINT 消 息,把客戶區(qū)所有無(wú)效部分合并成一個(gè)區(qū)域.合并WM_PAINT 消息節(jié)約了窗口必須重畫客戶區(qū)內(nèi)容的時(shí)間。
系統(tǒng)向線程消息隊(duì)列投遞消息是通過填充 一個(gè)MSG 結(jié)構(gòu),再把它復(fù)制到消息隊(duì)列中,MSG 結(jié)構(gòu)中的信息包括接收消息的窗口 句柄、消息標(biāo)識(shí)、兩個(gè)消息參數(shù)、消息時(shí)間以及鼠標(biāo)的位置,線程可把消息投遞到它自己的消息隊(duì)列中或是通過函數(shù) PostMessage 和PostThreadMessage 把 消息投遞到其它線程的隊(duì)列中去。
應(yīng) 用程序可通過函數(shù)GetMessage 從它的隊(duì)列中移走一條消息,應(yīng)用程序還可用函數(shù)PeekMessage 來 檢查隊(duì)列中的某個(gè)消息但并不移走它,這個(gè)函數(shù)用有關(guān)這條消息的信息填充MSG 結(jié)構(gòu)。
把一條消息從它的隊(duì)列中移走后.應(yīng)用程序可用函數(shù)DispatchMessage 指 導(dǎo)windows系統(tǒng)把這條消息發(fā)送到窗口過程進(jìn)行處理。DispatchMessage 利用前面調(diào)用函數(shù)GetMessage 或PeekMessage 時(shí) 填充的MSG 結(jié)構(gòu)的指針,把窗口句柄、消息標(biāo)識(shí)及兩個(gè)消息參數(shù)傳給窗口過程,但它并不傳送時(shí)間或鼠標(biāo)光標(biāo)的位置.應(yīng)用 程序可以在處理一條消息時(shí),通過調(diào)用函數(shù)GetMessageTime 和GetMessagePos 來 獲取這些信息。
一個(gè)線程可以 用函數(shù)WaitMessage 當(dāng)他沒有其他消息在其隊(duì)列里時(shí),產(chǎn)生對(duì)其他線程的控制。此函數(shù)將終止線程,直到一個(gè)新消 息被放入該線程的消息隊(duì)列里,然后返回。
你 可以調(diào)用函數(shù)SetMessageExtraInfo 來設(shè)置當(dāng)前線程消息隊(duì)列的附加信息。是和當(dāng)前線程的消息隊(duì)列聯(lián)系 的32位值。用戶可以用函數(shù)GetMessageExtraInfo 來獲得附加信息,該信息將會(huì)保留到下次調(diào)用函數(shù)GetMessage 或PeekMessage 之 前。
非排隊(duì)消息
非 排隊(duì)消息是直接發(fā)送到目標(biāo)窗口過程的,而不通過系統(tǒng)消息隊(duì)列和線程消息隊(duì)列。windows系統(tǒng)一般通過發(fā)送非排隊(duì)消息把影響某窗口的事件通知窗口。例 如,如果用戶激活一個(gè)新的應(yīng)用程序窗口.windows系統(tǒng)就會(huì)向該窗口發(fā)送一系列的消息,包括:WM_ACTIVATE ,WM_SETFOCUS 和WM_SETCURSOR ,這些消息分別通知窗口: 它被激活了;將通過這個(gè)窗口進(jìn)行鍵盤輸入;鼠標(biāo)已移到這個(gè)窗口邊 框的里面了 。非排隊(duì)消息也有可能發(fā)生在 應(yīng)用程序調(diào)用一個(gè)windows系統(tǒng)函數(shù)時(shí),例如,在應(yīng)用程序用函數(shù)SetWindowPos 來移動(dòng)一個(gè)窗口之 后,windows系統(tǒng)發(fā)送一條WM_WINDOWPOSCHANGED 消息。
應(yīng)用程序是調(diào)用函數(shù)SendMessage 、SendNotifyMessage 或SendDlgItemMessage 發(fā) 送消息的。
消息處理
應(yīng)用程序必須刪除和處理投遞到它的線 程消息隊(duì)列中的消息,單一線程的應(yīng)用程序一般是在它的WinMain函數(shù)中使用一個(gè)消息環(huán)來刪除消息,并把消息發(fā)送到相應(yīng)的窗口過程進(jìn)行處理。具有多重線 程的應(yīng)用程序在創(chuàng)建窗口的每一個(gè)線程中使用一個(gè)消息環(huán),下一節(jié)將講述消息環(huán)是如何工作的,另外還解釋了窗口過程的一般規(guī)則。
消息環(huán)
一個(gè)簡(jiǎn)單的消息環(huán)含有一個(gè)對(duì)下列函數(shù)的調(diào) 用:GetMessage, TranslateMessage和DispatchMessage。函數(shù)GetMessage從隊(duì)列中檢取一條消息并把它復(fù)制到一個(gè)MSG結(jié)構(gòu) 中.GetMessage應(yīng)返回TRUE,但如果它得到的是WM_QUIT消息,它就返回FALSE并結(jié)束循環(huán)。在單一線程的應(yīng)用程序中,結(jié)束消息循環(huán)通 常是關(guān)閉應(yīng)用程序的第一步。一般在應(yīng)用程序主窗口的窗口過程中響應(yīng)WM_DESTROY消息時(shí),應(yīng)用程序通過函數(shù)PostQuitMessage關(guān)閉它自 己的消息環(huán)。
如果在 GetMessage中指定窗口句柄,那么從隊(duì)列中檢取的只是指定窗口的消息。GetMessage 也能過濾隊(duì)列中的消息,這種情況下檢取的只是指定范圍內(nèi)的消息。有關(guān)過濾消息,參見 “消息過濾”。
如果某個(gè)線程想接收鍵盤的字符輸入,那么線程消息環(huán)中必須含有 TranslateMessage。Windows系統(tǒng)在用戶每按一次鍵時(shí)會(huì)產(chǎn)生一個(gè)虛鍵消息(WM_KEYDOWN和WM_KEYUP),虛鍵消息含有 一個(gè)標(biāo)識(shí)哪一個(gè)鍵被按過的虛鍵碼,但不是它的字符值,要得到這個(gè)值,消息環(huán)中必須含有TranslateMessage,由它來把虛鍵消息翻譯成字符消息 (WM_CHAR),再把它放回到應(yīng)用程序的消息隊(duì)列中去.這樣字符消息才能在消息環(huán)的下一輪循環(huán)中被發(fā)送到窗口過程。
函數(shù)DispatchMessage把消息發(fā)送到與MSG結(jié)構(gòu) 中指定的窗口句柄相應(yīng)的窗口過程,如果窗口句柄是HWND_TOPMOST ,DispatchMessage就把消息發(fā)送到系統(tǒng)中所有頂層窗口的窗口過程。如果窗口句柄是NULL,對(duì)于這條消息DispatchMessage則 什么也不做。
應(yīng)用程序的主線 程在初始化應(yīng)用程序并且至少創(chuàng)建了一個(gè)窗口之后就開始了消息循環(huán),一旦開始,消息環(huán)就連續(xù)不斷地從線程的消息隊(duì)列中校取消息并把它們分發(fā)到相應(yīng)的窗口,函 數(shù)GetMessage從消息隊(duì)列中檢取到WM_QUIT消息時(shí),消息環(huán)就結(jié)束了。
一個(gè)消息隊(duì)列只需要有一個(gè)消息環(huán),而不管應(yīng)用程序有多少個(gè)窗口,因?yàn)殛?duì)列中的每一條消息是一個(gè) MSG結(jié)構(gòu),其中含有接收消息的窗口句柄,DispatchMessage總能把消息發(fā)送到相應(yīng)的窗口。
應(yīng)用程序可以有多種方法修改它的消息環(huán),例如,它可以從隊(duì)列中檢取消息但并不 發(fā)送到任何窗口,這對(duì)那些投遞不指定窗口的消息的應(yīng)用程序是很有用的,(這些消息是提供給應(yīng)用程序的,而不是某個(gè)窗口,因?yàn)樗鼈兒蠳ULL窗口句柄)。 應(yīng)用程序也能指導(dǎo)GetMessage來搜索隊(duì)列中一個(gè)特定的消息,而不管其它消息,這對(duì)那些有時(shí)不按消息隊(duì)列先進(jìn)先出次序檢取消息的應(yīng)用程序來說是很有 用的。
使用鍵盤加速鍵的應(yīng)用 程序必須能夠把鍵盤消息轉(zhuǎn)換成命令消息,要這樣做,應(yīng)用程序的消息環(huán)必須調(diào)用函數(shù)TranslateAccelerator有關(guān)加速鍵,參見 “鍵盤加速鍵”。
窗口過程
窗口過程是一個(gè)函數(shù),用來接收和處理 所有發(fā)送到該窗口的消息,每個(gè)窗口類都有一個(gè)窗口過程,同一窗口類所創(chuàng)建的窗口共用同一個(gè)窗口過程來響應(yīng)消息。
系統(tǒng)通過把消息數(shù)據(jù)作為過程的參數(shù)來向窗口過程發(fā)送消息,再由窗口過程 完成與消息相應(yīng)的活動(dòng)。它需要檢查消息的標(biāo)識(shí),在處理消息時(shí)要使用由消息參數(shù)指定的這個(gè)信息。
窗口過程一般不會(huì)忽略—條消息,如果它不處理某條消息,它就必須把這條消息傳回系統(tǒng)進(jìn)行 默認(rèn)處理,窗口過程是調(diào)用函數(shù)DefWindowProc 來完成的,由它完成一個(gè)默認(rèn)的操作并返回消息結(jié)果。絕大多數(shù) 窗口過程只處理幾種類型的消息,其它的則通過調(diào)用DefWindowProc 傳給了系統(tǒng)。
因?yàn)榇翱谶^程是由所有屬于同類窗口共享的,所以它能處理 幾個(gè)不同窗口的消息,要識(shí)別受消息影響的某個(gè)窗口,窗口過程可以檢查消息所帶的窗口句柄。有關(guān)窗口過程,參見 “窗口過程”。
傳遞和發(fā)送消息
任何應(yīng)用程序都能投遞和發(fā)送消息,就跟系統(tǒng)一樣,應(yīng)用程序投遞一 條消息是通過把它復(fù)制到消息隊(duì)列,發(fā)送消息則是通過把消息數(shù)據(jù)作為窗門過程的參數(shù)。要投遞消息,應(yīng)用程 序需 要用到函數(shù)PostMessage ,要發(fā)送消息,程序使用函數(shù)SendMessage, BroadcastSystemMessage, SendMessageCallback, SendMessageTimeout, SendNotifyMessage 或SendDlgItemMessage 。
應(yīng)用程序通常投遞—條消息來通知某個(gè)窗口去完成 一個(gè)任務(wù)。PostMessage 為消息創(chuàng)建一個(gè)MSG 結(jié)構(gòu)并把消息拷到消息隊(duì)列 中,最后由應(yīng)用程序的消息環(huán)檢取這條消息再把它發(fā)送到相應(yīng)的窗口過程。
應(yīng)用程序一般是通過發(fā)送一條消息通知窗口過程立即完成某項(xiàng)任務(wù),函數(shù)SendMessage 把 消息發(fā)送到與給定窗口相應(yīng)的窗口過程,這個(gè)函數(shù)要等待窗口過程完成處理井返回消息的結(jié)果。父窗口與子窗口之間也是通過發(fā)送消息來進(jìn)行相互間的通信,例如, 某個(gè)父窗口有一個(gè)編輯控制框作為它的子窗口,就可通過向它發(fā)送消息設(shè)置控制框的正文,這個(gè)控制框則通過向父窗口發(fā)送消息來把用戶對(duì)正文的改變通知其父窗 口。
函數(shù)SendMessageCallback 也 能發(fā)送消息到指定窗口的窗口過程里,但是,這函數(shù)是立即返回,當(dāng)窗口過程函數(shù)處理完消息后,系統(tǒng)調(diào)用指定的回調(diào)函數(shù),有關(guān)更多回調(diào)函數(shù)信息,參考函數(shù)SendAsyncProc 。
有時(shí),應(yīng)用程序也有可能要求 向系統(tǒng)中的所有頂層窗口發(fā)送或投遞一條消息,例如,如果應(yīng)用程序改變了系統(tǒng)的時(shí)間,它必須通過發(fā)送WM_TIMECHANGE 消 息來通知所有頂層窗口,應(yīng)用程序向所有頂層窗口發(fā)送或投遞一條消息是調(diào)用函數(shù)SendMessage 或PostMessage , 并在hwnd 參數(shù)中指定HWND_TOPMOST 。你同樣通過函數(shù)BroadcastSystemMessage 指 定參數(shù)lpdwRecipients 的值為BSM_APPLICATIONS 來 廣 播消息給所有程序
應(yīng)用程序能 夠投遞一條消息而不指定窗口,在調(diào)用PostMessage 時(shí)應(yīng)用程序提供NULL 窗 口句柄,這條消息就被投遞到與當(dāng)前線程相應(yīng)的隊(duì)列中。因?yàn)闆]有指定窗口句柄,應(yīng)用程序就必須在處理消息環(huán)中的這條消息,這也是一種創(chuàng)建消息的方法.此類消 息適用于整個(gè)應(yīng)用程序,而不只是指某個(gè)窗口。
使用函數(shù)InSendMessage 窗口過程能夠確定它所處理的消息是從另一個(gè)線程發(fā)來的,這種能力 在需要根據(jù)消息源進(jìn)行消息處理時(shí)是很有用的。
經(jīng)常出現(xiàn)的一個(gè)編程錯(cuò)誤是假設(shè)函數(shù)PostMessage 總能成功地投遞一條消息,這在消息隊(duì)列是滿 的時(shí)候是不對(duì)的,應(yīng)用程序應(yīng)該檢查函數(shù)PostMessage 的返回值以確認(rèn)消息是否已經(jīng)被投遞,否則要重新投遞這條 消息。
消息種類
這部分將兩種類型windows消 息;系統(tǒng)定義的消息,程序定義的消息
系 統(tǒng)消息
系統(tǒng)使用系統(tǒng)定義的 消息來控制應(yīng)用程序的操作,并給應(yīng)用程序提供輸入或其他信息進(jìn)行處理。系統(tǒng)在與應(yīng)用程序進(jìn)行通信是時(shí)是發(fā)送系統(tǒng)消息的。應(yīng)用程序也能發(fā)送或投遞系統(tǒng)消息, 應(yīng)用程序通常用這些消息來控制預(yù)注冊(cè)類創(chuàng)建的控制窗口的操作。
每條系統(tǒng)消息都有一個(gè)唯一的消息標(biāo)識(shí),對(duì)應(yīng)于一個(gè)符號(hào)常量(在Windows系統(tǒng)頭文件中定義),它表明了消息的目的, 例如,常量WM_PAINT 要求窗口繪制它的內(nèi)容。
符號(hào)常量指定了系統(tǒng)消息所屬的類別,常量的前綴標(biāo)識(shí)能夠解釋和處理消息的畝口的類型。下表列出 了前綴及相應(yīng)的消息類別:
前 綴 消息類
ABM Application desktop toolbar
BM Button control
CB Combo box control
CDM Common dialog box
DBT Device
DL Drag list box
DM Default push button control
EM Edit control
HDM Header control
LB List box control
LVM List view control
PBM Progress bar
PSM Property sheet
SB Status bar window
SBM Scroll bar control
STM Static control
TB Toolbar
TBM Trackbar
TCM Tab control
TTM Tooltip control
TVM Tree-view control
UDM Up-down control
WM General window
通 用窗口消息覆蓋了—個(gè)較大范圍的信息和請(qǐng)求,包括鼠標(biāo)和鍵盤輸入消息、菜單和對(duì)話框輸入消息、窗口創(chuàng)建和管理消息及動(dòng)態(tài)數(shù)據(jù)交換消息(DDL)。
應(yīng)用程序定義消息
應(yīng)用程 序可創(chuàng)建用在它自己的窗口中的消息,或是與其它進(jìn)程中的窗口進(jìn)行通信的消息。如果應(yīng)用程序創(chuàng)建了它自己的消息,接收它們的窗口過程必須能夠?qū)ο⑦M(jìn)行翻 譯,并提供相應(yīng)的處理。 ’
windows 系統(tǒng)保留用于系統(tǒng)定義的消息的標(biāo)識(shí)值的范圍從0x0000到0x03FF(等于WM_USER —1)和0x8000到 0xBFFF應(yīng)用程序不能把這些值用于私有消息。
從0x0400(WM_USER 的值)到0x7FFF之間的值是可用于應(yīng)用程序定義的用于它自己 的消息標(biāo)識(shí),而從0xC000到0xFFFF之間的值是應(yīng)用程序?yàn)榱伺c其它應(yīng)用程序中的窗口進(jìn)行通信所定義的消息標(biāo)識(shí)。
應(yīng)用程序用函數(shù)RegisterWindowMessage 注 冊(cè)一條消息時(shí),windows系統(tǒng)返回的消息標(biāo)識(shí)在0xC000到0xFFFF之間,這個(gè)函數(shù)所返回的消息標(biāo)識(shí)應(yīng)保證在整個(gè)系統(tǒng)中是唯一的。如果應(yīng)用程序 要?jiǎng)?chuàng)建與其它應(yīng)用程序中的窗口進(jìn)行通信的消息,則使用RegisterWindowMessage 來對(duì)它進(jìn)行注冊(cè),這 個(gè)函數(shù)可防止由于其它的應(yīng)用程序基于不同的目的使用了相同的消息標(biāo)識(shí)所產(chǎn)生的沖突。
消息過濾
應(yīng)用程序可使用函數(shù)GetMessage 或PeekMessage 來 指定一個(gè)消息過濾器,從消息隊(duì)列中檢取指定的消息[忽略其它的消息),這是一個(gè)消息標(biāo)識(shí)的范圍(由第一個(gè)和最后一個(gè)標(biāo)識(shí)指定)、一個(gè)窗口句柄或者兩者都是GetMessage 和PeekMessage 利 用消息過濾器有選擇地檢取隊(duì)列中的某條消息。如果某個(gè)應(yīng)用程序必須檢索消息隊(duì)列中的排在后面的消息,消息過濾則是很有用的。
過濾消息的應(yīng)用程序必須保證滿足消息過濾器的消息是能被投 遞的,例如,如果某個(gè)應(yīng)用程序的過濾器用于一個(gè)并不接收鍵盤輸入的窗口中的WM_CHAR 消息,函數(shù)GetMessage 就 不能返回,這樣就會(huì)“掛起”這個(gè)應(yīng)用程序。
要 過濾鍵盤、鼠標(biāo)和DDE消息,應(yīng)用程序可以便用下列常量WM_KEYFIRST 和 WM_KEYLAST, WM_MOUSEFIRST 和 WM_MOUSELAST messages, 和 WM_DDE_FIRST 和 WM_DDE_LAST
消息死鎖
調(diào) 用函數(shù)SendMessage 的線程向另一個(gè)線程發(fā)送一條消息,要等待接收消息的窗口過程返回,如果接收消息的線程在 處理消息時(shí)放棄了控制,發(fā)送消息的線程就不能繼續(xù)執(zhí)行下去,因?yàn)樗却齋endMessage 返回,這種情況就叫做 死鎖。接收消息的線程無(wú)須直接地放棄控制,調(diào)用下列函數(shù)其個(gè)的一個(gè)就能讓線程放棄控制。
DialogBox
DialogBoxIndirect
DialogBoxIndirectParam
DialogBoxParam
GetMessage
MessageBox
PeekMessage
窗口過程可以確定它所接收的 消息是不是另一個(gè)線程通過調(diào)用函數(shù)InSendMessage 發(fā)來的。在處理一條消息時(shí)調(diào)用前面所列出的任一個(gè)函數(shù)之 前,窗口過程應(yīng)首先調(diào)用InSendMessage ,如果函數(shù)返回TRUE ,窗口 過程就必須在調(diào)用任何能使線程放棄控制的函數(shù)之前調(diào)用函數(shù)ReplyMessage 。
使用消息和消息隊(duì)列
這節(jié)描述如何完成下面的工作
創(chuàng)建消息環(huán)
檢查消息隊(duì)列
投遞消息
發(fā)送消息
創(chuàng)建消息環(huán)
windows 系統(tǒng)為每一個(gè)線程自動(dòng)創(chuàng)建消息隊(duì)列,如果線程創(chuàng)建了一個(gè)或多個(gè)窗口,就必須提供從線程消息隊(duì)列中檢取消息,并把它們發(fā)送至相應(yīng)窗口過程的消息環(huán)。
因?yàn)閣indows系統(tǒng)指導(dǎo)向應(yīng)用程 序中的某個(gè)窗口發(fā)送消息,線程就必須在啟動(dòng)它的消息環(huán)之前至少要?jiǎng)?chuàng)建—個(gè)窗口,絕大多數(shù)windows應(yīng)用程序含有一個(gè)創(chuàng)建窗口的線程。一個(gè)典型的應(yīng)用程 序是在函數(shù)WinMain 中注冊(cè)它主窗口的窗口類。創(chuàng)建和顯示主窗口.然后啟動(dòng)消息環(huán)。
函數(shù)GetMessage 和 DispatchMessage 用來創(chuàng)建消息環(huán),如果應(yīng)用程序必須從用戶得到字符輸入,那么在消息環(huán)中應(yīng)包含函數(shù)TranslateMessage , TranslateMessage 把 虛鍵消轉(zhuǎn)換成字符消息。下面的范例說明了一個(gè)簡(jiǎn)單的windows應(yīng)用程序的WinMain 函數(shù)中的消息環(huán)
HINSTANCE hinst;
HWND hwndMain;
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow)
{
MSG msg;
WNDCLASS wc;
UNREFERENCED_PARAMETER(lpszCmdLine);
// Register the window class for the main window.
if (!hPrevInstance)
{
wc.style = 0;
wc.lpfnWndProc = (WNDPROC) WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon((HINSTANCE) NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW);
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = "MainMenu";
wc.lpszClassName = "MainWndClass";
if (!RegisterClass(&wc))
return FALSE;
}
hinst = hInstance; // save instance handle
// Create the main window.
hwndMain = CreateWindow("MainWndClass", "Sample", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, (HWND) NULL, (HMENU) NULL, hinst, (LPVOID) NULL);
// If the main window cannot be created, terminate
// the application.
if (!hwndMain)
return FALSE;
// Show the window and paint its contents.
ShowWindow(hwndMain, nCmdShow);
UpdateWindow(hwndMain);
// Start the message loop.
while (GetMessage(&msg, (HWND) NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// Return the exit code to Windows.
return msg.wParam;
}
函數(shù)GetMessage,TranslateMessage 以 及DispatchMessage 把MSG 結(jié)構(gòu)的指針當(dāng)作一個(gè)參數(shù)。如果有消息,GetMessage 把 它復(fù)制到MSG 結(jié)構(gòu)中,如果這個(gè)消息是一條虛鍵消息(如WM_KEYDOWN 或WM_SYSKEYDOWN ),TranslateMessage 產(chǎn) 生一個(gè)字符消息(WM_CHAR 或WM_SYSCHAR ),并把它放到消息隊(duì)列中 去。DispatchMessage 也使用MSG 結(jié)構(gòu)的成員用作窗口過程的參數(shù). 但要等到窗口過程完成處理后才返回。
如 果某個(gè)線程支持加速鍵,那么它的消息環(huán)必須含有函數(shù)TranslateAccelerator 。這個(gè)函數(shù)檢查與線程加 速鍵表中的一個(gè)入口相匹配的組合鍵,如果它找到一個(gè)匹配值.TranslateAccelerator 就把組合鍵翻譯 成一條WM_COMMAND 消息,并把它發(fā)送到窗口過程。
如果某個(gè)線程使用模式對(duì)話框,消息環(huán)中必須含有函數(shù)IsDialogMessage 以 便于對(duì)話框能夠接收鍵盤輸入。
有 關(guān)對(duì)話框,參見“對(duì)話框”。
下 面的范例說明了一個(gè)使用加速鍵的線程的消息環(huán),其中顯示了一個(gè)模式對(duì)話框。
如果TranslateAccelerator 或IsDialogMessage 返 回TRUE (指示消息已被處理),就不再調(diào)用TranslateMessage 和 DispatchMessage 原因是TranslateAccelerator 或IsDialogMessage 完 成所有對(duì)消息的翻譯和發(fā)送工作。
HWND hwndMain;
HWND hwndDlgModeless = NULL;
MSG msg;
HACCEL haccel;
// Perform initialization and create a main window.
while (GetMessage(&msg, (HWND) NULL, 0, 0))
{
if (hwndDlgModeless == (HWND) NULL ||
!IsDialogMessage(hwndDlgModeless, &msg) &&
!TranslateAccelerator(hwndMain, haccel,
&msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
檢消息隊(duì)列
有時(shí),應(yīng)用程序需要在線程 消息環(huán)的外面檢查線程消息隊(duì)列的內(nèi)容,例如,如果某個(gè)應(yīng)用程序的窗口過程進(jìn)行一個(gè)較長(zhǎng)的繪畫 操作,就可能允許用戶中斷這個(gè)操作。除非應(yīng)用程序在處理鼠標(biāo)和鍵盤消息的操作過程中不停地檢查消息隊(duì)列,否則在操作結(jié)束之前就不再會(huì)響應(yīng)用戶的輸入.原因 是線程消息環(huán)中的函數(shù)DispatchMessage在窗口過程處理完消息之前是不會(huì)返回的。
對(duì)于一個(gè)較長(zhǎng)時(shí)間的操作,可使用函數(shù)PeekMessage來 檢查消息隊(duì)列,PeekMessage與函數(shù)GetMessage是很相似的,都 可用來檢查消息隊(duì)列中與過濾器標(biāo)準(zhǔn)相匹配的消息,再把這個(gè)消息復(fù)制到一個(gè)MSG結(jié)構(gòu)中。它們之間主要的不同就是GetMessage要 等待隊(duì)列中出現(xiàn)一條與過濾器標(biāo)準(zhǔn)相匹配的消息,而PeekMessage 會(huì)立即返回,不管隊(duì)列中是否有某條消息。
下面的范例說明了如何使用PeekMessage在 一個(gè)長(zhǎng)操作期間,檢查消息隊(duì)列中的一條單擊鼠標(biāo)或鍵盤輸入消息。
HWND hwnd;
BOOL fDone;
MSG msg;
// Begin the operation and continue until it is complete
// or until the user clicks the mouse or presses a key.
fDone = FALSE;
while (!fDone)
{
fDone = DoLengthyOperation(); // application-defined function
// Remove any messages that may be in the queue. If the
// queue contains any mouse or keyboard
// messages, end the operation.
while (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE))
{
switch(msg.message)
{
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_KEYDOWN:
// Perform any required cleanup.
fDone = TRUE;
}
}
}
其它的函數(shù)如GetQueueStatus和GetInputState也 能用來檢查線程消息隊(duì)列中的內(nèi)容,GetQueueStatus返回一組標(biāo)志,用來指明隊(duì)列中消息的類型,這是一個(gè)最 快的辦法來確定隊(duì)列中是否有消息,如果隊(duì)列中臺(tái)有鼠標(biāo)或鍵盤消息,GetInputState就返回TRUE, 這兩個(gè)函數(shù)都能用來確定隊(duì)列中是否有需要處理的消息。
投遞消息
使用函數(shù)PostMessage把一條消息投遞到消息隊(duì)列中,PostMessage在 線程消息隊(duì)列的最后放置消息并立即返回,它不等待線程處理這條消息。函數(shù)的參數(shù)包括窗口句柄、消息標(biāo)識(shí)相兩個(gè)消息參數(shù),windows系統(tǒng)把這些參數(shù)復(fù)制 到一個(gè)MSG結(jié)構(gòu)中,填充結(jié)構(gòu)的time和pt成 員,再把這個(gè)結(jié)構(gòu)放到消息隊(duì)列中。
windows 系統(tǒng)用函數(shù)PostMessage所帶的窗口句柄來決定哪一個(gè)線程消息隊(duì)列接收消息.如果句柄是HWND_TOPMOST,windows 系統(tǒng)就把這條消息投遞到所有頂層窗口的線程消息隊(duì)列中。
函數(shù)PostThreadMessage可用來向一個(gè)指定的線程消息隊(duì)列投遞消息,PostThreadMessage與PostMessage也 很相似.只是它的第一個(gè)參數(shù)是線程標(biāo)識(shí)而不是窗口句柄,可通過調(diào)用函數(shù)GetCurrentThreadId檢取這個(gè) 線程標(biāo)識(shí)。
函數(shù)PostQuitMessage用 來退出消息環(huán),PostQuitMessage向當(dāng)前正在執(zhí)行的線程發(fā)送WM_QUIT消 息,如果線程消息環(huán)接收到WM_QUIT消息,就結(jié)束消息環(huán)并把控制返回給windows系統(tǒng)。應(yīng)用程序通常調(diào)用PostQuitMessage響 應(yīng)WM_DESTROY消息,
用法如下
case WM_DESTROY:
// Perform cleanup tasks.
PostQuitMessage(0);
break;
發(fā)送消息
函數(shù)SendMessage是用來直接向 一個(gè)窗口過程發(fā)送消息,SendMessage調(diào)用一個(gè)窗口過程,并等待過程對(duì)消息的處理和返回結(jié)果。 一條消息可以被發(fā)往系統(tǒng)中的任何一個(gè)窗口,而僅要求有一個(gè)窗口句柄,windows系統(tǒng)用這個(gè)句柄決定哪一個(gè)窗口過程應(yīng)該接收這條消息。 如果窗口過程在處理由另一個(gè)線程發(fā)來的消息時(shí)放棄控制,就會(huì)出現(xiàn)消息死鎖(有關(guān)消息死鎖,參見 “消息死鎖”)。在處理一個(gè)可能是發(fā)自另一個(gè)線程的消息之前,窗口過程應(yīng)首先調(diào)用函數(shù)InSendMessage,如 果這個(gè)函數(shù)返回TRUE,那么窗口過程就應(yīng)在調(diào)用任何可以使線程放棄控制的函數(shù)之前調(diào)用ReplyMessage, 做法如下:
case WM_USER + 5:
if (InSendMessage())
ReplyMessage(TRUE);
DialogBox(hInst, "MyDialogBox", hwndMain, (DLGPROC) MyDlgProc);
break;
有一些消息可以發(fā)給對(duì)話框中的控制框,這些控制框消息設(shè)置控制框的外觀、特性和內(nèi)容或是檢取有關(guān) 控制框的信息。例如,CB_ADDSTRING消息可以向組合框添加字串,BM_SETCHECK消 息能夠設(shè)置復(fù)選框或單選按鈕的選擇狀態(tài)。
使 用函數(shù)SendDlgItemMessage向一個(gè)控制框發(fā)送一條消息,要指定控制框的標(biāo)識(shí),含有這個(gè)控制框的對(duì)話框 窗口的句柄。下面的范例用在對(duì)話框過程中的,把一個(gè)字串從組合框的編輯控制框拷到它的列表框,這個(gè)例子用SendDlgItemMessage向 組合框發(fā)送一條CB_ADDSTRING消息.
HWND hwndCombo;
int cTxtLen;
PSTR pszMem;
switch (uMsg)
{
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDD_ADDCBITEM:
// Get the handle of the combo box and the
// length of the string in the edit control
// of the combo box.
hwndCombo = GetDlgItem(hwndDlg, IDD_COMBO);
cTxtLen = GetWindowTextLength(hwndCombo);
// Allocate memory for the string and copy
// the string into the memory.
pszMem=(PSTR)VirtualAlloc((LPVOID)NULL,(DWORD)(cTxtLen+1),MEM_COMMIT, PAGE_READWRITE);
GetWindowText(hwndCombo, pszMem, cTxtLen + 1);
// Add the string to the list box of the
// combo box and remove the string from the
// edit control of the combo box.
if (*pszMem != NULL)
{
SendDlgItemMessage(hwndDlg, IDD_COMBO, CB_ADDSTRING, 0, (DWORD) ((LPSTR) pszMem));
SetWindowText(hwndCombo, (LPSTR) NULL);
}
// Free the memory and return.
VirtualFree(pszMem, 0, MEM_RELEASE);
return TRUE;
// Process other dialog box commands.
}
// Process other dialog box messages.
}
消息及消息隊(duì)列參考
下列函數(shù)函數(shù)用于消息和消息隊(duì)列
BroadcastSystemMessage
DefWindowProc
DispatchMessage
GetInputState
GetMessage
GetMessageExtraInfo
GetMessagePos
GetMessageTime
GetQueueStatus
InSendMessage
PeekMessage
PostMessage
PostQuitMessage
PostThreadMessage
RegisterWindowMessage
ReplyMessage
SendAsyncProc
SendMessage
SendMessageCallback
SendMessageTimeout
SendNotifyMessage
SetMessageExtraInfo
TranslateMessage
WaitMessage
Obsolete Functions
PostAppMessage
SetMessageQueue
WM_USER
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)
點(diǎn)擊舉報(bào)。