勾點概觀
勾點是應用程式可以攔截事件的機制,例如訊息、滑鼠動作和擊鍵。 攔截特定事件種類的函式稱為 攔截程式。 攔截程式可以對其接收的每個事件採取動作,然後修改或捨棄事件。
下列一些範例會針對勾點使用:
- 監視訊息以進行偵錯
- 提供錄製和播放宏的支援
- 提供協助金鑰的支援 (F1)
- 模擬滑鼠和鍵盤輸入
- 實作以電腦為基礎的訓練 (CBT) 應用程式
注意
勾點通常會使系統變慢,因為它們會增加系統必須針對每個訊息執行的處理量。 您應該只在必要時安裝勾點,並儘快將其移除。
本節將討論下列各項:
勾點鏈結
系統支援許多不同類型的勾點;每個類型都提供其訊息處理機制不同層面的存取權。 例如,應用程式可以使用 WH_MOUSE 勾點來監視滑鼠訊息的訊息流量。
系統會針對每種勾點類型維護個別的勾點鏈結。 勾點鏈結是稱為攔截程式之特殊應用程式定義回呼函式的指標清單。 當發生與特定類型勾點相關聯的訊息時,系統會將訊息傳遞至攔截鏈結中所參考的每個攔截程式,一個接著一個。 攔截程式可以採取的動作取決於涉及的勾點類型。 某些類型勾點的攔截程式只能監視訊息;其他人可以修改訊息,或透過鏈結停止進度,防止它們到達下一個攔截程式或目的地視窗。
攔截程式
為了利用特定類型的攔截,開發人員會提供攔截程式,並使用 SetWindowsHookEx 函式將它安裝到與攔截相關聯的鏈結中。 攔截程式必須具有下列語法:
LRESULT CALLBACK HookProc(
int nCode,
WPARAM wParam,
LPARAM lParam
)
{
// process event
...
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
HookProc 是應用程式定義名稱的預留位置。
nCode參數是攔截程式用來判斷要執行的動作的攔截程式碼。 攔截程式碼的值取決於攔截的類型;每個類型都有自己的攔截程式碼特性集。 wParam和lParam參數的值取決於攔截程式碼,但通常包含已傳送或張貼之訊息的相關資訊。
SetWindowsHookEx函式一律會在勾點鏈結的開頭安裝攔截程式。 當特定類型攔截所監視的事件發生時,系統會在與勾點相關聯的勾點鏈結開頭呼叫程式。 鏈結中的每個攔截程式都會判斷是否要將事件傳遞至下一個程式。 攔截程式會呼叫 CallNextHookEx 函式,將事件傳遞至下一個程式。
請注意,某些類型勾點的攔截程式只能監視訊息。 不論特定程式是否呼叫 CallNextHookEx,系統都會將訊息傳遞至每個攔截程式。
全域攔截會監視與呼叫執行緒相同桌面上所有線程的訊息。 執行緒特定的勾點只會監視個別執行緒的訊息。 全域攔截程式可以在與呼叫執行緒相同的桌面上的任何應用程式內容中呼叫,因此程式必須位於個別的 DLL 模組中。 只有在相關聯執行緒的內容中,才會呼叫執行緒特定的攔截程式。 如果應用程式為其中一個自己的執行緒安裝攔截程式,攔截程式可以位於與應用程式程式碼其餘部分相同的模組或 DLL 中。 如果應用程式為不同應用程式的執行緒安裝勾點程式,則程式必須位於 DLL 中。 如需詳細資訊,請參閱 Dynamic-Link 程式庫。
注意
您應該只針對偵錯目的使用全域勾點;否則,您應該避免它們。 全域勾點會損害系統效能,並導致與其他實作相同全域勾點類型的應用程式發生衝突。
勾點類型
每種勾點類型可讓應用程式監視系統訊息處理機制的不同層面。 下列各節說明可用的勾點。
- WH_CALLWNDPROC和WH_CALLWNDPROCRET
- WH_CBT
- WH_DEBUG
- WH_FOREGROUNDIDLE
- WH_GETMESSAGE
- WH_JOURNALPLAYBACK
- WH_JOURNALRECORD
- WH_KEYBOARD_LL
- WH_KEYBOARD
- WH_MOUSE_LL
- WH_MOUSE
- WH_MSGFILTER和WH_SYSMSGFILTER
- WH_SHELL
WH_CALLWNDPROC和WH_CALLWNDPROCRET
WH_CALLWNDPROC和WH_CALLWNDPROCRET勾點可讓您監視傳送至視窗程式的訊息。 系統會先呼叫 WH_CALLWNDPROC 攔截程式,再將訊息傳遞至接收視窗程式,並在視窗程式處理訊息之後呼叫 WH_CALLWNDPROCRET 攔截程式。
WH_CALLWNDPROCRET勾點會將CWPRETSTRUCT結構的指標傳遞至攔截程式。 結構包含來自處理訊息之視窗程式的傳回值,以及與訊息相關聯的訊息參數。 子類別化視窗不適用於進程之間設定的訊息。
如需詳細資訊,請參閱 CallWndProc 和 CallWndRetProc 回 呼函式。
WH_CBT
系統會先呼叫 WH_CBT 勾點程式,再啟動、建立、終結、最小化、最大化、移動或調整視窗大小;完成系統命令之前;從系統訊息佇列移除滑鼠或鍵盤事件之前;設定輸入焦點之前;或 與系統訊息佇列同步處理之前。 攔截程式傳回的值會決定系統是否允許或防止其中一項作業。 WH_CBT攔截主要用於以電腦為基礎的訓練 (CBT) 應用程式。
如需詳細資訊,請參閱 CBTProc 回 呼函式。
如需詳細資訊,請參閱 WinEvents。
WH_DEBUG
系統會先呼叫 WH_DEBUG 勾點程式,再呼叫與系統中任何其他勾點相關聯的攔截程式。 您可以使用這個勾點來判斷是否允許系統呼叫與其他類型的勾點相關聯的攔截程式。
如需詳細資訊,請參閱 DebugProc 回 呼函式。
WH_FOREGROUNDIDLE
WH_FOREGROUNDIDLE勾可讓您在其前景執行緒閒置時執行低優先順序的工作。 當應用程式的前景執行緒即將變成閒置時,系統會呼叫 WH_FOREGROUNDIDLE 攔截程式。
如需詳細資訊,請參閱 ForegroundIdleProc 回 呼函式。
WH_GETMESSAGE
WH_GETMESSAGE攔截可讓應用程式監視GetMessage或PeekMessage函式所傳回的訊息。 您可以使用 WH_GETMESSAGE 勾點來監視滑鼠和鍵盤輸入,以及其他張貼到訊息佇列的訊息。
如需詳細資訊,請參閱 GetMsgProc 回 呼函式。
WH_JOURNALPLAYBACK
警告
從 Windows 11 開始,不支援日誌攔截 API,將在未來的版本中移除。 因此,強烈建議改為呼叫 SendInput TextInput API。
WH_JOURNALPLAYBACK攔截可讓應用程式將訊息插入系統訊息佇列。 您可以使用此勾點來播放先前使用 WH_JOURNALRECORD所記錄的一系列滑鼠和鍵盤事件。 只要安裝 WH_JOURNALPLAYBACK 勾點,就會停用一般滑鼠和鍵盤輸入。 WH_JOURNALPLAYBACK勾點是全域勾點,無法當做執行緒特定的勾點使用。
WH_JOURNALPLAYBACK勾點會傳回逾時值。 這個值會告知系統在處理播放攔截的目前訊息之前,要等候多少毫秒。 這可讓勾點控制播放事件的時間。
如需詳細資訊,請參閱 JournalPlaybackProc 回 呼函式。
WH_JOURNALRECORD
警告
從 Windows 11 開始,不支援日誌攔截 API,將在未來的版本中移除。 因此,強烈建議改為呼叫 SendInput TextInput API。
WH_JOURNALRECORD攔截可讓您監視和記錄輸入事件。 一般而言,您可以使用這個勾點來記錄一連串的滑鼠和鍵盤事件,以便稍後使用 WH_JOURNALPLAYBACK播放。 WH_JOURNALRECORD勾點是全域勾點,無法當做執行緒特定的勾點使用。
如需詳細資訊,請參閱 JournalRecordProc 回 呼函式。
WH_KEYBOARD_LL
WH_KEYBOARD_LL攔截可讓您監視即將張貼線上程輸入佇列中的鍵盤輸入事件。
如需詳細資訊,請參閱 LowLevelKeyboardProc 回 呼函式。
WH_KEYBOARD
WH_KEYBOARD攔截可讓應用程式監視即將由GetMessage或PeekMessage函式傳回之WM_KEYDOWN和WM_KEYUP訊息的訊息流量。 您可以使用 WH_KEYBOARD 勾點來監視張貼至訊息佇列的鍵盤輸入。
如需詳細資訊,請參閱 KeyboardProc 回 呼函式。
WH_MOUSE_LL
WH_MOUSE_LL攔截可讓您監視即將張貼線上程輸入佇列中的滑鼠輸入事件。
如需詳細資訊,請參閱 LowLevelMouseProc 回 呼函式。
WH_MOUSE
WH_MOUSE勾可讓您監視即將由 GetMessage或PeekMessage函式傳回的滑鼠訊息。 您可以使用 WH_MOUSE 勾來監視張貼至訊息佇列的滑鼠輸入。
如需詳細資訊,請參閱 MouseProc 回 呼函式。
WH_MSGFILTER和WH_SYSMSGFILTER
WH_MSGFILTER和WH_SYSMSGFILTER勾可讓您監視即將由功能表、捲軸、訊息方塊或對話方塊處理的訊息,以及偵測使用者按下 ALT+TAB 或 ALT+ESC 按鍵組合而何時啟動不同的視窗。 WH_MSGFILTER勾點只能監視傳遞至安裝攔截程式之應用程式所建立功能表、捲軸、訊息方塊或對話方塊的訊息。 WH_SYSMSGFILTER攔截會監視所有應用程式的這類訊息。
WH_MSGFILTER和WH_SYSMSGFILTER勾點可讓您在模式迴圈期間執行訊息篩選,這相當於主要訊息迴圈中完成的篩選。 例如,應用程式通常會在擷取佇列訊息的時間與分派訊息的時間之間,檢查 main 迴圈中的新訊息,並適當地執行特殊處理。 不過,在強制回應迴圈期間,系統會擷取和分派訊息,而不允許應用程式在主要訊息迴圈中篩選訊息。 如果應用程式安裝 WH_MSGFILTER 或 WH_SYSMSGFILTER 攔截程式,則系統會在強制回應迴圈期間呼叫程式。
應用程式可以呼叫CallMsgFilter 函式,直接呼叫WH_MSGFILTER勾點。 透過使用此函式,應用程式可以在強制回應迴圈期間使用相同的程式碼來篩選訊息,如同在主要訊息迴圈中使用的一樣。 若要這樣做,請在WH_MSGFILTER攔截程式中封裝篩選作業,並在呼叫 GetMessage和DispatchMessage函式之間呼叫CallMsgFilter。
while (GetMessage(&msg, (HWND) NULL, 0, 0))
{
if (!CallMsgFilter(&qmsg, 0))
DispatchMessage(&qmsg);
}
CallMsgFilter的最後一個引數只會傳遞至攔截程式;您可以輸入任何值。 透過定義 MSGF_MAINLOOP等常數,攔截程式可以使用此值來判斷從何處呼叫程式。
如需詳細資訊,請參閱 MessageProc 和 SysMsgProc 回 呼函式。
WH_SHELL
殼層應用程式可以使用 WH_SHELL 勾點來接收重要通知。 當殼層應用程式即將啟動,以及建立或終結最上層視窗時,系統會呼叫 WH_SHELL 攔截程式。
請注意,自訂殼層應用程式不會接收 WH_SHELL 訊息。 因此,任何註冊本身為預設殼層的應用程式都必須呼叫 SystemParametersInfo 函式,才能 (或任何其他應用程式) 接收 WH_SHELL 訊息。 必須使用 SPI_SETMINIMIZEDMETRICS 和 MINIMIZEDMETRICS 結構呼叫此函式。 將這個 結構的 iArrange 成員設定為 ARW_HIDE。
如需詳細資訊,請參閱 ShellProc 回 呼函式。