Windows 消息機制詳解
zz from
• http://www.360doc.com/content/10/0531/23/1131125_30579505.shtml
總的來說:
MSG包括:
視窗句柄,訓示MSG發送的目的視窗
消息辨別
lPARAM、wParam
發送時間
發送時的滑鼠位置
關于消息隊列:
Windows系統有一個系統消息隊列
每個線程都有一個自己的消 息隊列(由于發送消息MSG需 要提供一個視窗HWnd,而基 本有視窗的線程,都是UI線 程),是以基本上如果線程使用了GDI函數,則windows給該線程配置設定一個線程消息隊列,這個消息隊列負責該線程的所有視窗的消息。
所有的視窗都有自己的句柄(HWND),消息被發送時,這個句柄就已經被指定了。是以 當子視窗收到一個消息時,其父視窗不會也收到這個消息,除非子視窗手 動的轉發。
消息分為:
實際上MSDN把消息分為隊列型(Queued Message)和非隊列型(Non-queued Message),這隻是不同的路由方式, 但最終都會由消息處理函數來處理。
隊 列型消息包括硬體的輸入(WM_KEY*等)、WM_TIMER消 息、WM_PAINT消息等;非隊列型的一些例子有WM_SETFOCUS, WM_ACTIVE, WM_SETCURSOR等,它們被直接發送給處理函數。
其實,按照MSDN的說法和消息的路由過程可以理 解為,Posted Message Queue裡的消息是真正的隊列型消 息,而通過SendMessage()發送到消息,即使它進入了Sent Message Queue,由于SendMessage要求的同步處理,這些消息也 應該算非隊列型消息。也許,Windows系統會特殊處理,使消息強行繞 過隊列。
=====================
一節較長的描述消息和消息隊列以及如何在 你程式中使用他們。
關于消息和消息 隊列
與傳統的應用程式不 同,Microsoft Windows應用程式并不顯式地用一個函數的調用(如c運作庫)來擷取輸入,而是,等待windows系統把輸入傳給它們。
windows系統把應用程式 的所有輸入傳給應用程式的視窗,每個視窗都有一個稱之為視窗過程的函數.當視窗有輸入時windows系統要調用它,視窗過程處理輸入并把控制傳回 windows系統。有關視窗過程,參見 “視窗過程”。 這一章講述消息及消息隊列,并說明在應用程式中如何使用它們。
消息
windows系統以消息的形式把輸入傳給視窗過程,消息是由windows系 統或應用程式産生的.windows系統對每一個輸入事件都要産生消息,例如,使用者按鍵盤、移動滑鼠或單擊一個滾動條控制框。windows系統為了響應 應用程式給系統帶來的變化也會産生消息,比如應用程式改變了系統字型資源池或是改變了一個窗門的大小。應用程式可通過産生消息指導它自己的視窗來完成某個 任務,或是與其它應用程式的視窗進行通信。
windows 系統把消息發送給視窗過程.視窗過程有四個參數:視窗句柄、消息辨別以及兩個叫做消息參數的32位值。視窗句柄決定消息将發送到哪—個窗 口,windows系統則用它來确定向哪一個視窗過程發送消息。
消息辨別是一個命名的常量,由它來标明消息的目的。如果視窗過程接收到一條消息,它就通過消息辨別來決定如何處理這條 消息。例如,消息辨別WM_PAINT 通知視窗過程,視窗的客戶區被改變了,需要重畫。
消息參數指定視窗過程在處理消息時所用的資料或資料的位 置,消息的意圖及數值取決了消息本身。消息參數可以是一個整數、緊縮的位标志、一個含有附加資料結構的指針等等。如果消息不使用消息參數,一般就都設定成NULL 、 視窗過程必須檢查消息辨別以确定如何解釋消息參數。
消息路由
windows系統用兩種方式向視窗過程發送消息:把消息投遞到一個先進先出的消息隊列中,它是一個系統定義的記憶體塊用于臨時存儲消息;或是把消 息直接發給視窗過程。
投遞到 消息隊列中的消息叫排隊消息,它們主要是使用者通過滑鼠或鍵盤的輸入結果.如WM_MOUSEMOVE, WM_LBUTTONDOWN, WM_KEYDOWN, and WM_CHAR 消息。其它的排隊消息包括定時器、繪制和退出消息:WM_TIMER, WM_PAINT, and WM_QUIT 。所有直接發送到視窗過程的其它消息稱之為非排隊消息。
排隊消息
windows系統在同一時間可顯示多個視窗,要發送滑鼠和鍵盤輸入到相應 的視窗,windows系統要用到消息隊列,它要管理一個系統消息隊列和任意數目線程消息隊列,每一個隊列對應于一個線程。
不管什麼時候,隻要使用者移動滑鼠或是敲鍵盤.滑鼠或鍵盤的 裝置驅動器都要把輸入轉換成消息,并把它們放到系統消息隊列中去。windows從系統隊列中每次移走一條消息,确定目的視窗,再把它們投遞到建立目的窗 口的線程的消息隊列中,線程消息隊列接收所有由該線程建立的視窗的滑鼠和鍵盤消息。線程從它的隊列中移走消息并指導windows系統将它們發送到相應的 視窗過程進行處理。有關線程,參見 “程序和線程”。
WM_PAINT 消息有點特别,windows系統總是把這條消息放在消息隊列的最後,這樣 可保證視窗按先進先出次序接收它的輸入消息,WM_PAINT 消息被保持在隊列中,隻有在隊列中沒有其它消息時才發送 到視窗過程。同一個視窗的多個WM_PAINT 消息被合并成一個WM_PAINT 消 息,把客戶區所有無效部分合并成一個區域.合并WM_PAINT 消息節約了視窗必須重畫客戶區内容的時間。
系統向線程消息隊列投遞消息是通過填充 一個MSG 結構,再把它複制到消息隊列中,MSG 結構中的資訊包括接收消息的視窗 句柄、消息辨別、兩個消息參數、消息時間以及滑鼠的位置,線程可把消息投遞到它自己的消息隊列中或是通過函數 PostMessage 和PostThreadMessage 把 消息投遞到其它線程的隊列中去。
應 用程式可通過函數GetMessage 從它的隊列中移走一條消息,應用程式還可用函數PeekMessage 來 檢查隊列中的某個消息但并不移走它,這個函數用有關這條消息的資訊填充MSG 結構。
把一條消息從它的隊列中移走後.應用程式可用函數DispatchMessage 指 導windows系統把這條消息發送到視窗過程進行處理。DispatchMessage 利用前面調用函數GetMessage 或PeekMessage 時 填充的MSG 結構的指針,把視窗句柄、消息辨別及兩個消息參數傳給視窗過程,但它并不傳送時間或滑鼠光标的位置.應用 程式可以在處理一條消息時,通過調用函數GetMessageTime 和GetMessagePos 來 擷取這些資訊。
一個線程可以 用函數WaitMessage 當他沒有其他消息在其隊列裡時,産生對其他線程的控制。此函數将終止線程,直到一個新消 息被放入該線程的消息隊列裡,然後傳回。
你 可以調用函數SetMessageExtraInfo 來設定目前線程消息隊列的附加資訊。是和目前線程的消息隊列聯系 的32位值。使用者可以用函數GetMessageExtraInfo 來獲得附加資訊,該資訊将會保留到下次調用函數GetMessage 或PeekMessage 之 前。
非排隊消息
非 排隊消息是直接發送到目标視窗過程的,而不通過系統消息隊列和線程消息隊列。windows系統一般通過發送非排隊消息把影響某視窗的事件通知視窗。例 如,如果使用者激活一個新的應用程式視窗.windows系統就會向該視窗發送一系列的消息,包括:WM_ACTIVATE ,WM_SETFOCUS 和WM_SETCURSOR ,這些消息分别通知視窗: 它被激活了;将通過這個視窗進行鍵盤輸入;滑鼠已移到這個視窗邊 框的裡面了 。非排隊消息也有可能發生在 應用程式調用一個windows系統函數時,例如,在應用程式用函數SetWindowPos 來移動一個視窗之 後,windows系統發送一條WM_WINDOWPOSCHANGED 消息。
應用程式是調用函數SendMessage 、SendNotifyMessage 或SendDlgItemMessage 發 送消息的。
消息處理
應用程式必須删除和處理投遞到它的線 程消息隊列中的消息,單一線程的應用程式一般是在它的WinMain函數中使用一個消息環來删除消息,并把消息發送到相應的視窗過程進行處理。具有多重線 程的應用程式在建立視窗的每一個線程中使用一個消息環,下一節将講述消息環是如何工作的,另外還解釋了視窗過程的一般規則。
消息環
一個簡單的消息環含有一個對下列函數的調 用:GetMessage, TranslateMessage和DispatchMessage。函數GetMessage從隊列中檢取一條消息并把它複制到一個MSG結構 中.GetMessage應傳回TRUE,但如果它得到的是WM_QUIT消息,它就傳回FALSE并結束循環。在單一線程的應用程式中,結束消息循環通 常是關閉應用程式的第一步。一般在應用程式主視窗的視窗過程中響應WM_DESTROY消息時,應用程式通過函數PostQuitMessage關閉它自 己的消息環。
如果在 GetMessage中指定視窗句柄,那麼從隊列中檢取的隻是指定視窗的消息。GetMessage 也能過濾隊列中的消息,這種情況下檢取的隻是指定範圍内的消息。有關過濾消息,參見 “消息過濾”。
如果某個線程想接收鍵盤的字元輸入,那麼線程消息環中必須含有 TranslateMessage。Windows系統在使用者每按一次鍵時會産生一個虛鍵消息(WM_KEYDOWN和WM_KEYUP),虛鍵消息含有 一個辨別哪一個鍵被按過的虛鍵碼,但不是它的字元值,要得到這個值,消息環中必須含有TranslateMessage,由它來把虛鍵消息翻譯成字元消息 (WM_CHAR),再把它放回到應用程式的消息隊列中去.這樣字元消息才能在消息環的下一輪循環中被發送到視窗過程。
函數DispatchMessage把消息發送到與MSG結構 中指定的視窗句柄相應的視窗過程,如果視窗句柄是HWND_TOPMOST ,DispatchMessage就把消息發送到系統中所有頂層視窗的視窗過程。如果視窗句柄是NULL,對于這條消息DispatchMessage則 什麼也不做。
應用程式的主線 程在初始化應用程式并且至少建立了一個視窗之後就開始了消息循環,一旦開始,消息環就連續不斷地從線程的消息隊列中校取消息并把它們分發到相應的視窗,函 數GetMessage從消息隊列中檢取到WM_QUIT消息時,消息環就結束了。
一個消息隊列隻需要有一個消息環,而不管應用程式有多少個視窗,因為隊列中的每一條消息是一個 MSG結構,其中含有接收消息的視窗句柄,DispatchMessage總能把消息發送到相應的視窗。
應用程式可以有多種方法修改它的消息環,例如,它可以從隊列中檢取消息但并不 發送到任何視窗,這對那些投遞不指定視窗的消息的應用程式是很有用的,(這些消息是提供給應用程式的,而不是某個視窗,因為它們含有NULL視窗句柄)。 應用程式也能指導GetMessage來搜尋隊列中一個特定的消息,而不管其它消息,這對那些有時不按消息隊列先進先出次序檢取消息的應用程式來說是很有 用的。
使用鍵盤加速鍵的應用 程式必須能夠把鍵盤消息轉換成指令消息,要這樣做,應用程式的消息環必須調用函數TranslateAccelerator有關加速鍵,參見 “鍵盤加速鍵”。
視窗過程
視窗過程是一個函數,用來接收和處理 所有發送到該視窗的消息,每個視窗類都有一個視窗過程,同一視窗類所建立的視窗共用同一個視窗過程來響應消息。
系統通過把消息資料作為過程的參數來向視窗過程發送消息,再由視窗過程 完成與消息相應的活動。它需要檢查消息的辨別,在處理消息時要使用由消息參數指定的這個資訊。
視窗過程一般不會忽略—條消息,如果它不處理某條消息,它就必須把這條消息傳回系統進行 預設處理,視窗過程是調用函數DefWindowProc 來完成的,由它完成一個預設的操作并傳回消息結果。絕大多數 視窗過程隻處理幾種類型的消息,其它的則通過調用DefWindowProc 傳給了系統。
因為視窗過程是由所有屬于同類視窗共享的,是以它能處理 幾個不同視窗的消息,要識别受消息影響的某個視窗,視窗過程可以檢查消息所帶的視窗句柄。有關視窗過程,參見 “視窗過程”。
傳遞和發送消息
任何應用程式都能投遞和發送消息,就跟系統一樣,應用程式投遞一 條消息是通過把它複制到消息隊列,發送消息則是通過把消息資料作為窗門過程的參數。要投遞消息,應用程 序需 要用到函數PostMessage ,要發送消息,程式使用函數SendMessage, BroadcastSystemMessage, SendMessageCallback, SendMessageTimeout, SendNotifyMessage 或SendDlgItemMessage 。
應用程式通常投遞—條消息來通知某個視窗去完成 一個任務。PostMessage 為消息建立一個MSG 結構并把消息拷到消息隊列 中,最後由應用程式的消息環檢取這條消息再把它發送到相應的視窗過程。
應用程式一般是通過發送一條消息通知視窗過程立即完成某項任務,函數SendMessage 把 消息發送到與給定視窗相應的視窗過程,這個函數要等待視窗過程完成處理井傳回消息的結果。父視窗與子視窗之間也是通過發送消息來進行互相間的通信,例如, 某個父視窗有一個編輯控制框作為它的子視窗,就可通過向它發送消息設定控制框的正文,這個控制框則通過向父視窗發送消息來把使用者對正文的改變通知其父窗 口。
函數SendMessageCallback 也 能發送消息到指定視窗的視窗過程裡,但是,這函數是立即傳回,當視窗過程函數處理完消息後,系統調用指定的回調函數,有關更多回調函數資訊,參考函數SendAsyncProc 。
有時,應用程式也有可能要求 向系統中的所有頂層視窗發送或投遞一條消息,例如,如果應用程式改變了系統的時間,它必須通過發送WM_TIMECHANGE 消 息來通知所有頂層視窗,應用程式向所有頂層視窗發送或投遞一條消息是調用函數SendMessage 或PostMessage , 并在hwnd 參數中指定HWND_TOPMOST 。你同樣通過函數BroadcastSystemMessage 指 定參數lpdwRecipients 的值為BSM_APPLICATIONS 來 廣 播消息給所有程式
應用程式能 夠投遞一條消息而不指定視窗,在調用PostMessage 時應用程式提供NULL 窗 口句柄,這條消息就被投遞到與目前線程相應的隊列中。因為沒有指定視窗句柄,應用程式就必須在處理消息環中的這條消息,這也是一種建立消息的方法.此類消 息适用于整個應用程式,而不隻是指某個視窗。
使用函數InSendMessage 視窗過程能夠确定它所處理的消息是從另一個線程發來的,這種能力 在需要根據消息源進行消息處理時是很有用的。
經常出現的一個程式設計錯誤是假設函數PostMessage 總能成功地投遞一條消息,這在消息隊列是滿 的時候是不對的,應用程式應該檢查函數PostMessage 的傳回值以确認消息是否已經被投遞,否則要重新投遞這條 消息。
消息種類
這部分将兩種類型windows消 息;系統定義的消息,程式定義的消息
系 統消息
系統使用系統定義的 消息來控制應用程式的操作,并給應用程式提供輸入或其他資訊進行處理。系統在與應用程式進行通信是時是發送系統消息的。應用程式也能發送或投遞系統消息, 應用程式通常用這些消息來控制預注冊類建立的控制視窗的操作。
每條系統消息都有一個唯一的消息辨別,對應于一個符号常量(在Windows系統頭檔案中定義),它表明了消息的目的, 例如,常量WM_PAINT 要求視窗繪制它的内容。
符号常量指定了系統消息所屬的類别,常量的字首辨別能夠解釋和處理消息的畝口的類型。下表列出 了字首及相應的消息類别:
前 綴 消息類
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
通 用視窗消息覆寫了—個較大範圍的資訊和請求,包括滑鼠和鍵盤輸入消息、菜單和對話框輸入消息、視窗建立和管理消息及動态資料交換消息(DDL)。
應用程式定義消息
應用程 序可建立用在它自己的視窗中的消息,或是與其它程序中的視窗進行通信的消息。如果應用程式建立了它自己的消息,接收它們的視窗過程必須能夠對消息進行翻 譯,并提供相應的處理。 ’
windows 系統保留用于系統定義的消息的辨別值的範圍從0x0000到0x03FF(等于WM_USER —1)和0x8000到 0xBFFF應用程式不能把這些值用于私有消息。
從0x0400(WM_USER 的值)到0x7FFF之間的值是可用于應用程式定義的用于它自己 的消息辨別,而從0xC000到0xFFFF之間的值是應用程式為了與其它應用程式中的視窗進行通信所定義的消息辨別。
應用程式用函數RegisterWindowMessage 注 冊一條消息時,windows系統傳回的消息辨別在0xC000到0xFFFF之間,這個函數所傳回的消息辨別應保證在整個系統中是唯一的。如果應用程式 要建立與其它應用程式中的視窗進行通信的消息,則使用RegisterWindowMessage 來對它進行注冊,這 個函數可防止由于其它的應用程式基于不同的目的使用了相同的消息辨別所産生的沖突。
消息過濾
應用程式可使用函數GetMessage 或PeekMessage 來 指定一個消息過濾器,從消息隊列中檢取指定的消息[忽略其它的消息),這是一個消息辨別的範圍(由第一個和最後一個辨別指定)、一個視窗句柄或者兩者都是GetMessage 和PeekMessage 利 用消息過濾器有選擇地檢取隊列中的某條消息。如果某個應用程式必須檢索消息隊列中的排在後面的消息,消息過濾則是很有用的。
過濾消息的應用程式必須保證滿足消息過濾器的消息是能被投 遞的,例如,如果某個應用程式的過濾器用于一個并不接收鍵盤輸入的視窗中的WM_CHAR 消息,函數GetMessage 就 不能傳回,這樣就會“挂起”這個應用程式。
要 過濾鍵盤、滑鼠和DDE消息,應用程式可以便用下列常量WM_KEYFIRST 和 WM_KEYLAST, WM_MOUSEFIRST 和 WM_MOUSELAST messages, 和 WM_DDE_FIRST 和 WM_DDE_LAST
消息死鎖
調 用函數SendMessage 的線程向另一個線程發送一條消息,要等待接收消息的視窗過程傳回,如果接收消息的線程在 處理消息時放棄了控制,發送消息的線程就不能繼續執行下去,因為它正等待SendMessage 傳回,這種情況就叫做 死鎖。接收消息的線程無須直接地放棄控制,調用下列函數其個的一個就能讓線程放棄控制。
DialogBox
DialogBoxIndirect
DialogBoxIndirectParam
DialogBoxParam
GetMessage
MessageBox
PeekMessage
視窗過程可以确定它所接收的 消息是不是另一個線程通過調用函數InSendMessage 發來的。在處理一條消息時調用前面所列出的任一個函數之 前,視窗過程應首先調用InSendMessage ,如果函數傳回TRUE ,視窗 過程就必須在調用任何能使線程放棄控制的函數之前調用函數ReplyMessage 。
使用消息和消息隊列
這節描述如何完成下面的工作
建立消息環
檢查消息隊列
投遞消息
發送消息
建立消息環
windows 系統為每一個線程自動建立消息隊列,如果線程建立了一個或多個視窗,就必須提供從線程消息隊列中檢取消息,并把它們發送至相應視窗過程的消息環。
因為windows系統指導向應用程 序中的某個視窗發送消息,線程就必須在啟動它的消息環之前至少要建立—個視窗,絕大多數windows應用程式含有一個建立視窗的線程。一個典型的應用程 序是在函數WinMain 中注冊它主視窗的視窗類。建立和顯示主視窗.然後啟動消息環。
函數GetMessage 和 DispatchMessage 用來建立消息環,如果應用程式必須從使用者得到字元輸入,那麼在消息環中應包含函數TranslateMessage , TranslateMessage 把 虛鍵消轉換成字元消息。下面的範例說明了一個簡單的windows應用程式的WinMain 函數中的消息環
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;
}
函數GetMessage,TranslateMessage 以 及DispatchMessage 把MSG 結構的指針當作一個參數。如果有消息,GetMessage 把 它複制到MSG 結構中,如果這個消息是一條虛鍵消息(如WM_KEYDOWN 或WM_SYSKEYDOWN ),TranslateMessage 産 生一個字元消息(WM_CHAR 或WM_SYSCHAR ),并把它放到消息隊列中 去。DispatchMessage 也使用MSG 結構的成員用作視窗過程的參數. 但要等到視窗過程完成處理後才傳回。
如 果某個線程支援加速鍵,那麼它的消息環必須含有函數TranslateAccelerator 。這個函數檢查與線程加 速鍵表中的一個入口相比對的組合鍵,如果它找到一個比對值.TranslateAccelerator 就把組合鍵翻譯 成一條WM_COMMAND 消息,并把它發送到視窗過程。
如果某個線程使用模式對話框,消息環中必須含有函數IsDialogMessage 以 便于對話框能夠接收鍵盤輸入。
有 關對話框,參見“對話框”。
下 面的範例說明了一個使用加速鍵的線程的消息環,其中顯示了一個模式對話框。
如果TranslateAccelerator 或IsDialogMessage 返 回TRUE (訓示消息已被處理),就不再調用TranslateMessage 和 DispatchMessage 原因是TranslateAccelerator 或IsDialogMessage 完 成所有對消息的翻譯和發送工作。
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);
}
}
檢消息隊列
有時,應用程式需要線上程 消息環的外面檢查線程消息隊列的内容,例如,如果某個應用程式的視窗過程進行一個較長的繪畫 操作,就可能允許使用者中斷這個操作。除非應用程式在處理滑鼠和鍵盤消息的操作過程中不停地檢查消息隊列,否則在操作結束之前就不再會響應使用者的輸入.原因 是線程消息環中的函數DispatchMessage在視窗過程處理完消息之前是不會傳回的。
對于一個較長時間的操作,可使用函數PeekMessage來 檢查消息隊列,PeekMessage與函數GetMessage是很相似的,都 可用來檢查消息隊列中與過濾器标準相比對的消息,再把這個消息複制到一個MSG結構中。它們之間主要的不同就是GetMessage要 等待隊列中出現一條與過濾器标準相比對的消息,而PeekMessage 會立即傳回,不管隊列中是否有某條消息。
下面的範例說明了如何使用PeekMessage在 一個長操作期間,檢查消息隊列中的一條單擊滑鼠或鍵盤輸入消息。
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;
}
}
}
其它的函數如GetQueueStatus和GetInputState也 能用來檢查線程消息隊列中的内容,GetQueueStatus傳回一組标志,用來指明隊列中消息的類型,這是一個最 快的辦法來确定隊列中是否有消息,如果隊列中台有滑鼠或鍵盤消息,GetInputState就傳回TRUE, 這兩個函數都能用來确定隊列中是否有需要處理的消息。
投遞消息
使用函數PostMessage把一條消息投遞到消息隊列中,PostMessage在 線程消息隊列的最後放置消息并立即傳回,它不等待線程處理這條消息。函數的參數包括視窗句柄、消息辨別相兩個消息參數,windows系統把這些參數複制 到一個MSG結構中,填充結構的time和pt成 員,再把這個結構放到消息隊列中。
windows 系統用函數PostMessage所帶的視窗句柄來決定哪一個線程消息隊列接收消息.如果句柄是HWND_TOPMOST,windows 系統就把這條消息投遞到所有頂層視窗的線程消息隊列中。
函數PostThreadMessage可用來向一個指定的線程消息隊列投遞消息,PostThreadMessage與PostMessage也 很相似.隻是它的第一個參數是線程辨別而不是視窗句柄,可通過調用函數GetCurrentThreadId檢取這個 線程辨別。
函數PostQuitMessage用 來退出消息環,PostQuitMessage向目前正在執行的線程發送WM_QUIT消 息,如果線程消息環接收到WM_QUIT消息,就結束消息環并把控制傳回給windows系統。應用程式通常調用PostQuitMessage響 應WM_DESTROY消息,
用法如下
case WM_DESTROY:
// Perform cleanup tasks.
PostQuitMessage(0);
break;
發送消息
函數SendMessage是用來直接向 一個視窗過程發送消息,SendMessage調用一個視窗過程,并等待過程對消息的處理和傳回結果。 一條消息可以被發往系統中的任何一個視窗,而僅要求有一個視窗句柄,windows系統用這個句柄決定哪一個視窗過程應該接收這條消息。 如果視窗過程在處理由另一個線程發來的消息時放棄控制,就會出現消息死鎖(有關消息死鎖,參見 “消息死鎖”)。在處理一個可能是發自另一個線程的消息之前,視窗過程應首先調用函數InSendMessage,如 果這個函數傳回TRUE,那麼視窗過程就應在調用任何可以使線程放棄控制的函數之前調用ReplyMessage, 做法如下:
case WM_USER + 5:
if (InSendMessage())
ReplyMessage(TRUE);
DialogBox(hInst, "MyDialogBox", hwndMain, (DLGPROC) MyDlgProc);
break;
有一些消息可以發給對話框中的控制框,這些控制框消息設定控制框的外觀、特性和内容或是檢取有關 控制框的資訊。例如,CB_ADDSTRING消息可以向組合框添加字串,BM_SETCHECK消 息能夠設定複選框或單選按鈕的選擇狀态。
使 用函數SendDlgItemMessage向一個控制框發送一條消息,要指定控制框的辨別,含有這個控制框的對話框 視窗的句柄。下面的範例用在對話框過程中的,把一個字串從組合框的編輯控制框拷到它的清單框,這個例子用SendDlgItemMessage向 組合框發送一條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.
}
消息及消息隊列參考
下列函數函數用于消息和消息隊列
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