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