設置全局鍵盤鉤子和消息鉤子

前幾天因爲工作需要用到鉤子,而且是全局鉤子,用來監視別人的程序鍵盤消息。需要注意幾點
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);
}




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