深入GetMessage和PeekMessage【2】

 處理系統隊列中的鼠標事件

下面是處理鼠標事件的步驟。

1.  首先,將計算該事件屏幕座標的相應窗體。此計算(調用窗體點擊測試)以桌面窗體開始,從頭至尾的掃描細統中的每一個窗體(包括子窗體),直到找到一個包含這個鼠標座標點的窗體,並且這個窗體沒有任何同樣包含這個座標點的子窗體。

圖2. 鼠標事件的窗體點擊測試

例如:如果圖2中的箭頭代表當前的鼠標位置,任何的鼠標行爲,像單擊鼠標鍵,將生成一個會在B窗體中產生消息的事件。

2.  如果一個窗體使用SetCapture捕獲鼠標,那麼“系統隊列掃描”代碼將通過普通的點擊測試,並將所有的鼠標消息返回到捕獲的窗體。例如:如果在圖2 中的A窗體調用了SetCapture,則在箭頭所指位置的所有鼠標行爲,將產生窗體A中的消息,而不是窗體B。

3.  如果這個被處理的事件是一個“鼠標鍵按下”事件(任何一個鼠標鍵),代碼會檢測這個事件是否會轉化爲雙擊事件。你可以在微軟開發者網絡(譯者注:MSDN)CD(技術文章,Ask Dr. GUI)中的“Ask Dr. GUI #5”中找到關於雙擊轉化的描述。實質上,如果在兩次鼠標鍵按下事件中,時間和距離的增量在允許的範圍之中,該事件將會生成一個雙擊消息,否則它將生成一個標準的“按下”事件。所有的鼠標事件都將生成標準的鼠標消息,而雙擊測試只在鼠標事件指定的,包含CS_DBLCLKS類型的窗體中進行。

4.  一個消息從鼠標事件中構造出來。

5.  如果鼠標點擊測試確定該事件發生在一個窗體的非客戶區,如邊框或標題欄,那麼該構造出的消息映射到它相應的非客戶區消息中。例如:一個WM_MOUSEMOVE事件會被映謝爲WM_NCMOUSEMOVE消息。

6.  與所有指定的消息過濾器進行對照,覈查此消息。(請參閱下面的“消息範圍過濾和窗體句柄過濾”)如果該消息不匹配過濾器,則重新從頭開始“系統隊列掃描”代碼,查看隊列中的下一個消息。

7.  如 果鼠標消息需要前往與當前任務不同的另一個任務的相關窗體,事件會被留在系統隊列中,並且如果那個將會處理這個消息的任務在休眠之中,會被喚醒。這個新近 被喚醒的任務不會在此刻立即運行,只會標記爲準備運行。如果消息前往了其它任務,並且在系統隊列中沒有要處理的事件被發現,“系統隊列掃描”會代碼返回到GetMessage/PeekMessage主代碼。請參閱下面的“讓步與休眠的不同”以獲得更多的信息。

8.  如果安裝了鼠標鉤子,它將在此刻被調用。如果鼠標鉤子返回了一個非零值,那麼該鼠標事件被忽略,並從系統隊列中被刪除,然後重新從頭開始“系統隊列掃描”代碼。如果鉤子返回零,則繼續處理。

9.  如果消息是一個“鼠標鍵按下”消息,“系統隊列掃描”則會在返回此消息之前,按照下面的方法激活窗體。

?它沿着父鏈一直向上尋找該窗體的“最終頂層父窗體”,直到相遇。

?它用SendMessage向該窗體的“最終頂層父窗體”發送一個WM_MOUSEACTIVATE消息。

?從WM_MOUSEACTVATE返回的值將在下面被測試:

a)       如果返回的值爲空、MA_ACTIVATE或者MA_ACTIVATEANDEAT,ActivateWindow函數將被調用去激活那個“最終頂層父窗體”。

b)      如果返回的值是MA_NOACTIVATE或者MA_NOACTIVATEANDEAT,窗體則不被激活。

注意:MA_ACTIVATEANDEAT和MA_NOACTIVATEANDEAT會導致“鼠標鍵按下”事件從系統隊列中被刪除,而不會生成一個鼠標按下消息。

c)      最終,一個WM_SETCURSOR消息被髮送到窗體,充許窗體設置指針的輪廓。

10. 如果鼠標鉤子被調用,並且當前的鼠標事件從系統隊列中被刪除了,則檢查“基於計算機訓練”(CBT)的鉤子。如果安裝有有一個CBT鉤子,將會攜帶HCBT_CLICKSKIPPED鉤子碼代調用它。

11. 按鍵狀態表包含了三個用於跟蹤鼠標按鍵狀態的入口。這些按鍵被分配予虛擬鍵代碼(VK_LBUTTON,VK_RUTTON和VC_MBUTTON),它們和GetKeyState一起始用去確事實上鼠標鍵是彈起還是按下。在返回鼠標消息之前,“系統隊列掃描”代碼會(爲彈起或按下消息)設置按鍵狀態表並且從系統隊列中刪除消息。如果PeekMessage被調用時攜帶PM_NOREMOVE,則按鍵狀態表不會被修改。

 

處理系統隊列中的鍵盤事件

1.  檢查是否Ctrl鍵被按下和當前的事件是否ESC鍵按。如果是,該用戶——直接窗體——會顯示任務管理器窗體。一個WM_SYSCOMMAND消息將被髮送到激活的窗體,並且參數wParam爲SC_TASKLIT。然後鍵盤按下事件從系統隊列中被刪除,“系統隊列掃描”代碼又將重新從頭開始。如果此激活的窗體是一個系統模塊或者是一個被顯示出來的“硬”系統模塊消息框(比如一個“INT”24小時系統錯誤消息框,或一個使用MB_ICONHAND和MB_SYSTEMMODAL參數的MessageBox函數)的事件,將會被拋棄。

2.  下一步,試着去查看當前的事件是不是一個Print Screen鍵的按下事件。如果是,任意一個激活的窗體或整個桌面將被做爲一個位圖快照,保存到剪貼板中。如果Alt鍵被按下,一幅激活窗體的圖像被複制到剪貼板中;如果沒有,則是整個桌面被複制。然後Print Screen鍵按下事件從系統隊列中被刪除,“系統隊列掃描”代碼又將重新從頭開始。如果顯示了一個“硬”系統模塊消息框,則此操作被忽略。

3.  下一步檢測熱鍵。使用程序管理器,用戶可以定義用來運行一個應用程序的擊鍵事件。這些擊鍵被稱爲熱鍵。如果當前的事件是一個按鍵事件,將會被測試是否與定義過的熱鍵匹配。如果發現匹配,一個WM_SYSCOMMAND消息將被髮送到激活的窗體,並且參數wParam爲SC_HOTKEY。然後鍵盤按下事件從系統隊列中被刪除,“系統隊列掃描”代碼又將重新從頭開始。如果此激活的窗體是一個系統模塊或者是一個被顯示出來的“硬”系統模塊消息框,該測試被跳過。

4.  一般情況下,所有的鍵盤消息(如WM_KEYDOWN、WM_CHAR等等)前往具有輸入焦點的窗體。如果這個具有輸入焦點的窗體與另一個當前執行的任務相關聯,那麼該事件會被留在系統隊列中,並且那個擁有“有焦點的窗體”的任務會被喚醒(如果休眠了)。“系統隊列掃描”代碼會像沒要發現任何要處理的事件一樣,返回到主GetMessage/PeekMessage代碼。請參閱下面的“讓步與休眠的不同”和“應用程序如何被喚醒”以獲得更多的信息。

5.  如果遇到了沒有任何一個窗體具有輸入焦點的情形,鍵盤消息會直接前往當前激活的窗體,而不會被翻譯成爲系統鍵消息(如WM_SYSKEYDOW,WM_SYSCHAR,等等)。

6.  與所有指定的消息過濾器進行對照,覈查此消息。(請參閱下面的“消息範圍過濾和窗體句柄過濾”)如果該消息不匹配過濾器,則重新從頭開始“系統隊列掃描”代碼,查看隊列中的下一個消息。

7.  如果事件被返到了當前的任務,它將從系統隊列中被刪除掉,除非PeekMessage被指定爲PM_NOREMOVE標記。請參閱下面的“PeekMessage的PM_NOREMOVE標記”以瞭解更多的關於不從隊列中刪除事件的信息。

8.  如果安裝有鍵盤鉤子,將在此刻被調用。如果事件從系統隊列中被刪除了,鉤子的調用將伴隨HC_ACTION屬性;如果事件未被從系統隊列中刪除,鉤子的調用將具有HC_NOREM屬性。

9.  如果鍵盤鉤子被調用,並且當前的按鍵事件從系統隊列中被刪除了,則檢查現存的CBT鉤子。如果安裝有CBT鉤子,將調用它並攜帶HCBT_KEYSKIPPED鉤子碼。

10.              最後,消息被返加到主GetMessage/PeekMessage代碼。

 

PeekMessage與PM_NOREMOVE

默認情況下,每一個消息被返回到應用程序後,PeekMessage和 GetMessage都會把消息和事件從系統隊列中刪除。然而有些時候,某個應用程序可能需要掃描隊列中現存的消息而並不刪除它們。例如,某個應用程序在做一些處理過程,這些處理過程期望“一但發現有可用的消息,就儘快終止”。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章