前幾天因爲工作需要用到鉤子,而且是全局鉤子,用來監視別人的程序鍵盤消息。需要注意幾點
1.如果只需要監視自己的應用程序,在自己程序裏設置鉤子就行。
2.如果需要監視別人的應用程序,一定要做成DLL形式的全局鉤子。
首先我是用VS新建一個DLL 工程。頭文件如下
#ifndef __KEYHOOK_H_H
#define __KEYHOOK_H_H
#ifdef MY_HOOK_API
#else
#define MY_HOOK_API _declspec(dllimport)
#endif
#ifdef __cplusplus
extern "C" {
#endif
MY_HOOK_API BOOL __stdcall Reg_raw_input(HWND hwnd);//註冊輸入信息,這個可以不要,因爲自己工作的原因需要加上
MY_HOOK_API BOOL __stdcall InstallHook();
MY_HOOK_API BOOL __stdcall UnInstallHook();
#ifdef __cplusplus
}
#endif
#endif
.CPP文件
自己使用RegisterRawInputDevices這個函數時有一個小問題卡了很久,用這個RIDEV_INPUTSINK標誌時(即窗口不在前臺時也臺捕捉輸入源信息),微軟文檔明確說明不用指定hwndTarget,試了很多次也不成功。然後直接有DLL用FindWindows找自己需要的句柄傳給hwndTarget,還是返回失敗,只有設置RIM_INPUT纔可以,最後偶爾把上層應用的主窗口GetSafeWind()返回的句柄給hwndTarget時才成功。
BOOL __stdcall Reg_raw_input(HWND hwnd)
{
static bool bReg_raw_input = false;
if(bReg_raw_input)
return FALSE;
RAWINPUTDEVICE dev;
memset(&dev, 0, sizeof(dev));
dev.usUsagePage = 1;
dev.usUsage = 6;
dev.dwFlags = RIDEV_INPUTSINK;
//dev.dwFlags = RIDEV_INPUTSINK;//RIM_INPUT;//Shejn 2015/7/17 16:34:31 add
dev.hwndTarget = hwnd;
BOOL bReg = RegisterRawInputDevices(&dev, 1, sizeof(dev));
bReg_raw_input = true;
return bReg;
};
BOOL __stdcall InstallHook()
{
BOOL bRet = 1;
UProductCmd cmd;
bNewKey = cmd.IsUseKeyNewMap();
_hHook_msg1 = SetWindowsHookEx(WH_GETMESSAGE, msg_hook_Proc1,g_hInst, NULL);//GetCurrentThreadId());最後一個參數爲0表明監視所有線程而不是本身
if (_hHook_msg1 == NULL)
{
bRet = 0;
}
g_hHook = SetWindowsHookEx(WH_KEYBOARD_LL,//這裏使用底層鍵盤鉤子,在鍵盤動產生作用前就響應,而且底層鉤子比消息鉤子先響應
LowLevelKeyboardProc,
g_hInst,
NULL);
if (g_hHook == NULL)
{
bRet = 0;
}
return bRet;
//return g_hHook == NULL ? FALSE : TRUE;
}
BOOL __stdcall UnInstallHook()
{
UnhookWindowsHookEx(_hHook_msg1);
return UnhookWindowsHookEx(g_hHook);
}
BOOL WINAPI DllMain(IN HINSTANCE hDllHandle,
IN DWORD nReason,
IN LPVOID Reserved)
{
//g_hInst = hDllHandle;
//return TRUE;
switch (nReason)
{
case DLL_PROCESS_ATTACH:
g_hInst=HINSTANCE(hDllHandle);
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
//UnInstallHook();//這個要註釋掉,後來發現如果監視的程序關掉,會直接調用這裏,導致全局鉤子失效,需要卸載鉤子時在上層應用關閉時調用
break;
}
return TRUE;
}
下面這段代碼注意,如果是回車和空格互換,比如VK_RETURN變成VK_SPACE,keybd_event(VK_SPACE, 0 0, 0);似乎還會鍵盤鉤子捕獲到VK_SPACE,爲了保險起見,最好設置一個標誌,如果是第二個keybd_event產生,則直接return CallNextHookEx(g_hHook, code, wParam, lParam); 這點還沒確認,高手可以去確認下。
DWORD dwIndex = 0;
BYTE g_bFreezeCAVK[2] = {VK_F2, VK_F8};
LRESULT CALLBACK LowLevelKeyboardProc(
int code,
WPARAM wParam,
LPARAM lParam)
{
if(code == HC_ACTION)
{
PKBDLLHOOKSTRUCT pStruct = (PKBDLLHOOKSTRUCT)lParam;
switch (pStruct->vkCode)
{
case VK_RETURN:
dwIndex = 0;
break;
case VK_SPACE:
dwIndex = 1;
break;
default:
bWork = FALSE;
return CallNextHookEx(g_hHook, code, wParam, lParam);
}
if (wParam == WM_KEYDOWN)
{
keybd_event(g_bVK[dwIndex], MapVirtualKey(g_bVK[dwIndex], 0), 0, 0);
return 1;//CallNextHookEx(g_hHook, code, wParam, lParam);//這裏注意返回1是原按鍵無效,如果是CallNextHookEx則會斷續傳遞還會產生原按鍵消息
}
}
return CallNextHookEx(g_hHook, code, wParam, lParam);
}
//消息鉤子,這段代碼可以忽略,根據自己的需要來
LRESULT CALLBACK msg_hook_Proc1(int nCode, WPARAM wParam, LPARAM lParam)
{
MSG* pMsg = (MSG*)lParam;
if(nCode < 0)
return CallNextHookEx(_hHook_msg1, nCode, wParam, lParam);
if(pMsg->message == WM_INPUT)
{
HRAWINPUT hInput = (HRAWINPUT)pMsg->lParam;
CString strDev_name = get_dev_name(hInput);
UProductCmd cmd;
CString strMini_kbd_id = cmd.get_setting("keyboard", "MiniKeyboardID", "Vid_04d9&Pid_1603");
strMini_kbd_id.MakeLower();
strDev_name.MakeLower();
int aTest = strDev_name.Find(strMini_kbd_id);
if(strDev_name.Find(strMini_kbd_id) >= 0 && bNewKey)//cmd.IsUseKeyNewMap())
{
_bHid = true;
}
else
{
_bHid = false;
}
//bWork = TRUE;
if (bWork)
{
if(_bHid)
{
if (bFreeze)
{
keybd_event(g_bFreezeCAVK[dwIndex], MapVirtualKey(g_bFreezeCAVK[dwIndex], 0), 0, 0);
}
else
keybd_event(g_bLiveCAVK[dwIndex], MapVirtualKey(g_bLiveCAVK[dwIndex], 0), 0, 0);
//keybd_event(g_bCAVK[dwIndex], MapVirtualKey(g_bCAVK[dwIndex], 0), 0, 0);
if (dwIndex == 0)
{
bFreeze = !bFreeze;
}
}
else
{
bKeep = TRUE;
if (bNewKey)
{
keybd_event(g_bNewVK[dwIndex], MapVirtualKey(g_bNewVK[dwIndex], 0), 0, 0);
}
else
{
keybd_event(g_bOldVK[dwIndex], MapVirtualKey(g_bOldVK[dwIndex], 0), 0, 0);
}
}
}
bWork = FALSE;
}
return CallNextHookEx(_hHook_msg1, nCode, wParam, lParam);
}