使用鍵盤鼠標監(jiān)控鉤子
本節(jié)將介紹如何使用Windows API
中的SetWindowsHookEx
和RegisterHotKey
函數(shù)來實現(xiàn)鍵盤鼠標的監(jiān)控。這些函數(shù)可以用來設置全局鉤子,通過對特定熱鍵掛鉤實現(xiàn)監(jiān)控的效果,兩者的區(qū)別在于SetWindowsHookEx
函數(shù)可以對所有線程進行監(jiān)控,包括其他進程中的線程,而RegisterHotKey
函數(shù)只能對當前線程進行監(jiān)控。
首先我們來實現(xiàn)注冊熱鍵功能,注冊熱鍵可以使用RegisterHotKey()
函數(shù),該函數(shù)可以將一個熱鍵與當前應用程序或線程綁定,使得當用戶按下熱鍵時,系統(tǒng)會自動將該熱鍵的消息發(fā)送到該應用程序或線程中,該函數(shù)原型如下;
BOOL RegisterHotKey( HWND hWnd, int id, UINT fsModifiers, UINT vk);
其中,參數(shù)的含義如下:
hWnd:熱鍵所屬的窗口句柄,通常設置為NULL,表示與當前線程綁定
id:熱鍵的ID號,用于區(qū)分不同的熱鍵
fsModifiers:熱鍵的修飾鍵,可以使用組合鍵,例如
Ctrl
、Alt
、Shift
等vk:熱鍵的虛擬鍵碼,例如
VK_F1
表示F1
鍵VK_LEFT
表示左箭頭鍵等
函數(shù)需要傳入一個窗口句柄、熱鍵ID、熱鍵組合鍵等參數(shù)來設置熱鍵。當熱鍵被按下時,系統(tǒng)會自動將一個WM_HOTKEY
消息發(fā)送給注冊了該熱鍵的窗口,口,應用程序需要重載該窗口的消息處理函數(shù)來響應該事件,從而實現(xiàn)相應的響應操作。該函數(shù)會返回一個BOOL
類型的值,表示熱鍵設置是否成功。
當熱鍵被注冊后則就需要接收熱鍵消息,通??梢允褂?code>GetMessage函數(shù),該函數(shù)用于從消息隊列中獲取一個消息并將其存儲在一個結(jié)構(gòu)體中,通常用于在一個循環(huán)中不斷地獲取消息,從而實現(xiàn)對Windows
消息的處理。
該函數(shù)的原型定義如下所示;
BOOL GetMessage( LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax);
其中,參數(shù)的含義如下:
lpMsg:指向MSG結(jié)構(gòu)體的指針,用于存儲獲取到的消息
hWnd:消息接收者的窗口句柄,通常設置為NULL,表示接收所有窗口的消息
wMsgFilterMin:指定獲取消息的最小消息值,通常設置為0
wMsgFilterMax:指定獲取消息的最大消息值,通常設置為0
GetMessage函數(shù)需要傳入一個指向MSG結(jié)構(gòu)體的指針,該結(jié)構(gòu)體包含了消息的各種信息,例如消息的類型、發(fā)送者、接收者、時間戳等等。讀者只需要通過判斷函數(shù)內(nèi)的WM_HOTKEY消息,并監(jiān)控是否為我們所需要的即可,如下代碼是一段注冊熱鍵的實現(xiàn),分別注冊了Ctrl+F1, Ctrl+F2, Ctrl+F3三個熱鍵組;
#include <iostream>#include <Windows.h>using namespace std;int main(int argc, char* argv[]){ // 分別注冊三個熱鍵 Ctrl+F1 , Ctrl+F2 , Ctrl+F3 if (0 == RegisterHotKey(NULL, 1, MOD_CONTROL, VK_F1)) { cout << GetLastError() << endl; } if (0 == RegisterHotKey(NULL, 2, MOD_CONTROL, VK_F2)) { cout << GetLastError() << endl; } if (0 == RegisterHotKey(NULL, 3, MOD_CONTROL, VK_F3)) { cout << GetLastError() << endl; } //做此線程的消息循環(huán),查出所有窗口的WM_HOTKEY消息,實際業(yè)務我們就是在消息處理過程中處理 MSG msg = { 0 }; while (::GetMessage(&msg,NULL,0,0)) { if (msg.message == WM_HOTKEY) { if (msg.wParam == 1) { std::cout << "CTRL + F1" << std::endl; } else if(msg.wParam == 2) { std::cout << "CTRL + F2" << std::endl; } else if (msg.wParam == 3) { std::cout << "CTRL + F3" << std::endl; } } } return 0;}
當然上述方法是局部的,讀者只能在當前進程內(nèi)使用,如果離開了進程窗體則這類熱鍵將會失效,此時我們就需要使用SetWindowsHookEx
函數(shù)注冊全局鉤子,該函數(shù)可以在系統(tǒng)中安裝鉤子,以便監(jiān)視或攔截特定的事件或消息。
以下是SetWindowsHookEx
的函數(shù)原型:
HHOOK SetWindowsHookEx( int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId);
參數(shù)說明:
idHook:鉤子類型,可以是WH_KEYBOARD(鍵盤鉤子)或WH_MOUSE(鼠標鉤子)等
lpfn:回調(diào)函數(shù),當特定事件或消息發(fā)生時,操作系統(tǒng)會調(diào)用此函數(shù)。該函數(shù)的返回值由鉤子類型和參數(shù)決定
hMod:包含
lpfn
的DLL
句柄。如果lpfn
參數(shù)在當前進程內(nèi),則該參數(shù)可以為NULLdwThreadId:線程標識符,指定與鉤子相關(guān)聯(lián)的線程。如果
dwThreadId
參數(shù)為0,則鉤子將應用于所有線程
函數(shù)會返回一個類型為HHOOK
的句柄,該句柄可以在卸載鉤子時使用,讀者需要注意由于全局鉤子會影響系統(tǒng)性能,因此在使用SetWindowsHookEx
函數(shù)時應謹慎,并在使用結(jié)束后及時的通過UnhookWindowsHookEx
釋放鉤子句柄。
如下所示代碼則是一個鍵盤鉤子監(jiān)控案例,在該案例中我們通過SetWindowsHookEx
注冊一個全局鉤子,并設置回調(diào)函數(shù)LowLevelKeyboardProc
通過使用PeekMessageA
監(jiān)控鍵盤事件,當有鍵盤事件產(chǎn)生時則自動路由到LowLevelKeyboardProc
函數(shù)內(nèi),此時即可得到按鍵的類型以及按下鍵位,如下所示;
#include <iostream>#include <Windows.h>using namespace std;//鍵盤鉤子句柄HHOOK keyboardHook = NULL;// 鍵盤鉤子LRESULT CALLBACK LowLevelKeyboardProc(_In_ int nCode, _In_ WPARAM wParam, _In_ LPARAM lParam){ KBDLLHOOKSTRUCT* ks = (KBDLLHOOKSTRUCT*)lParam; if (ks->flags == 128 || ks->flags == 129) { //監(jiān)控按鍵狀態(tài) if (nCode >= 0) { if (wParam == WM_KEYDOWN) { cout << "普通按鍵抬起" << endl; } else if (wParam == WM_KEYUP) { cout << "普通按鍵按下" << endl; } else if (wParam == WM_SYSKEYDOWN) { cout << "系統(tǒng)按鍵抬起" << endl; } else if (wParam == WM_SYSKEYUP) { cout << "系統(tǒng)按鍵按下" << endl; } } } if (ks->vkCode == 0x41) { cout << "檢測到按鍵:" << "A" << endl; } else if (ks->vkCode == 0x0D) { cout << "檢測到按鍵:" << "Enter" << endl; } else if (ks->vkCode == 0xA0 || ks->vkCode == 0xA1) { cout << "檢測到按鍵:" << "Shift" << endl; } else if (ks->vkCode == 0x08) { cout << "檢測到按鍵:" << "Backspace(刪除鍵)" << endl; } else if (ks->vkCode == 0x20) { cout << "檢測到按鍵:" << "Space(空格鍵)" << endl; } // 直接返回1可以使按鍵失效 //return 1; return CallNextHookEx(NULL,nCode,wParam,lParam);}int main(int argc, char* argv[]){ // 安裝鍵盤鉤子,WH_KEYBOARD_LL為鍵盤鉤子 keyboardHook = ::SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandle(NULL), 0); if (!keyboardHook) { cout << "無法注冊鍵盤鉤子!" << endl; return -1; } MSG msg = { 0 }; while (1) { if (::PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE)) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); } else { //... } } ::UnhookWindowsHookEx(keyboardHook); return 0; return 0;}
鼠標鉤子的掛鉤與鍵盤基本一致,只是在調(diào)用SetWindowsHookEx
傳遞參數(shù)時設置了WH_MOUSE_LL
鼠標事件,當有鼠標消息時則通過MouseProc
鼠標回調(diào)函數(shù)執(zhí)行,
#include <iostream>#include <Windows.h>using namespace std;//鼠標鉤子句柄HHOOK mouseHook = NULL;// 鼠標鉤子LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam){ MSLLHOOKSTRUCT* ms = (MSLLHOOKSTRUCT*)lParam; POINT pt = ms->pt; DWORD mouseData = ms->time; LPCSTR tipMsg = NULL; if (nCode >= 0) { if (wParam == WM_MOUSEMOVE) { tipMsg = "鼠標 [移動]"; } else if (wParam == WM_LBUTTONDOWN) { tipMsg = "鼠標 [左鍵] 按下"; } else if (wParam == WM_LBUTTONUP) { tipMsg = "鼠標 [左鍵] 抬起"; } else if(wParam == WM_LBUTTONDBLCLK) { tipMsg = "鼠標 [左鍵] 雙擊"; } else if (wParam == WM_RBUTTONDOWN) { tipMsg = "鼠標 [右鍵] 按下"; } else if (wParam == WM_RBUTTONUP) { tipMsg = "鼠標 [右鍵] 抬起"; } else if (wParam == WM_RBUTTONDBLCLK) { tipMsg = "鼠標 [右鍵] 雙擊"; } else if (wParam == WM_MBUTTONDOWN) { tipMsg = "鼠標 [滾輪] 按下"; } else if (wParam == WM_MBUTTONUP) { tipMsg = "鼠標 [滾輪] 抬起"; } else if (wParam == WM_MBUTTONDBLCLK) { tipMsg = "鼠標 [滾輪] 雙擊"; } else if (wParam == WM_MOUSEWHEEL) { tipMsg = "鼠標 [滾輪] 滾動"; } } std::cout << "鼠標狀態(tài): " << tipMsg << std::endl; std::cout << "X: " << pt.x << " Y: " << pt.y << std::endl; std::cout << "附加數(shù)據(jù): " << mouseData << std::endl; return CallNextHookEx(NULL, nCode, wParam, lParam); }int main(int argc, char* argv[]){ mouseHook = ::SetWindowsHookEx(WH_MOUSE_LL, MouseProc, GetModuleHandle(NULL), NULL); if (!mouseHook) { cout << "錯誤!安裝鼠標鉤子失??!" << endl; } MSG msg = { 0 }; while (true) { if (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { // 把按鍵消息傳遞給字符消息 ::TranslateMessage(&msg); //將消息分派給窗口程序 ::DispatchMessage(&msg); } else { //some thing todo .. } } UnhookWindowsHookEx(mouseHook); return 0;}
讀者可自行編譯并運行上述代碼片段,當掛鉤后我們就可以看到鼠標的移動位置以及鼠標擊鍵情況,如下圖所示;
// 把按鍵消息傳遞給字符消息 ::TranslateMessage(&msg); //將消息分派給窗口程序 ::DispatchMessage(&msg); } else { //some thing todo .. } } UnhookWindowsHookEx(mouseHook); return 0; }
讀者可自行編譯并運行上述代碼片段,當掛鉤后我們就可以看到鼠標的移動位置以及鼠標擊鍵情況,如下圖所示;文章來源:http://www.zghlxwxcb.cn/news/detail-766037.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-766037.html
到了這里,關(guān)于使用Windows API實現(xiàn)鍵盤鼠標監(jiān)控鉤子的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!