失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > MFC 全局钩子dll注入监听键盘消息

MFC 全局钩子dll注入监听键盘消息

时间:2021-11-20 03:45:51

相关推荐

MFC 全局钩子dll注入监听键盘消息

MFC自带的键盘监听功能只有焦点在MFC程序界面时才能监听键盘消息,要想在MFC程序界面外监听键盘消息,可以通过DLL注入使用全局钩子来监听。首先,通过Visual Studio生成全局钩子的动态库,再在MFC程序中调用动态库。

一、生成dll

1.Visual Studio新建空项目(为了方便调试,我将dll项目保存在调用项目里新建的Hook文件夹中)

2.项目属性将配置类型设为动态库,字符集设为Unicod字符集(如果设为多字节字符集,部分代码需作修改)

3.创建Keyboard.h和Keyboard.cpp这两个文件

(1)Keyboard.h声明组合键类和键位值

#pragma once#ifdef MATHLIBRARY_EXPORTS#define MATHLIBRARY_API __declspec(dllexport)#else#define MATHLIBRARY_API __declspec(dllimport)#endif#include <iostream>#include "windows.h"//#include <conio.h>using namespace std;#pragma warning(disable:4996)//extern "C" MATHLIBRARY_API 从cpp中将变量、类和函数导入到h中,这样调用dll的程序才能使用这些变量、类和函数//extern "C":外部变量声明,MATHLIBRARY_API指本dll,只有声明外部变量、外部类和外部函数,调用dll的程序才能使用这些变量、类和函数//外部变量的声明以及定义必需写在cpp文件上,再extern "C" MATHLIBRARY_API,这样dll和调用dll的程序才能使用这些变量、类和函数extern "C" MATHLIBRARY_API BOOL InstallHook(HWND callframe);extern "C" MATHLIBRARY_API BOOL UninstallHook();WH_KEYBOARD时的键值wParam//#define F1 173//#define F2 174//#define F3 175//#define F4 115//#define F5 116//#define F6 54//#define F7 118//#define F8 119//#define F9 120//#define F10 121//#define F11 122//#define F12 123////#define Letter_A 65//#define Letter_B 66//#define Letter_C 67//#define Letter_D 68//#define Letter_E 69//#define Letter_F 70//#define Letter_G 71//#define Letter_H 72//#define Letter_I 73//#define Letter_J 74//#define Letter_K 75//#define Letter_L 76//#define Letter_M 77//#define Letter_N 78//#define Letter_O 79//#define Letter_P 80//#define Letter_Q 81//#define Letter_R 82//#define Letter_S 83//#define Letter_T 84//#define Letter_U 85//#define Letter_V 86//#define Letter_W 87//#define Letter_X 88//#define Letter_Y 89//#define Letter_Z 90////#define Tab 9//#define CapsLk 20//#define Wave 192//·~//#define Ctrl 17BYTE Fn = FALSE; //Fn无法监听,按Fn+F1-F12切换到F1-F12,否则键位功能为红色字体上方字体代表的功能//#define Win 91//#define Alt 18//#define Enter 13//#define Shift 16//#define Space 32//#define Esc 27//#define Insert 45//#define NumLock 144//#define PrtSc 44//#define Delete 46//#define Home 36//#define End 35//#define PgUp 33//#define PgDn 34//#define BackSpace 8//#define Mouse_RButton 93 //空格键右边第二个键位,有鼠标右键的作用////#define Operator_divide 111// / == ?///#define Operator_plus 107// +//#define Operator_multiply 106// *//#define Operator_subtract 109// -//#define Decimal_point 110//#define Number_0 96//#define Number_1 97//#define Number_2 98//#define Number_3 99//#define Number_4 100//#define Number_5 101//#define Number_6 102//#define Number_7 103//#define Number_8 104//#define Number_9 105////#define Left 37//#define Right 39//#define Up 38//#define Down 40Letter area//#define LetterArea_number_0 48// ) //#define LetterArea_number_1 49// !//#define LetterArea_number_2 50// @//#define LetterArea_number_3 51// #//#define LetterArea_number_4 52// $//#define LetterArea_number_5 53// %//#define LetterArea_number_6 54// ^//#define LetterArea_number_7 55// &//#define LetterArea_number_8 56// *//#define LetterArea_number_9 57// (//#define subtract 189// -_//#define plus_equal 187// + =//#define Left_brackets 219// [{//#define Right_brackets 221// ]}//#define Stop_sign 220// \| //#define Semicolon 186// ;://#define Quotation_marks 222// '"//#define Comma 188// ,<//#define Full_stop 190// .>//WH_KEYBOARD_LL时的键值vkCode//BYTE Fn = FALSE; //Fn无法监听,按Fn+F1-F12切换到F1-F12,否则键位功能为红色字体上方字体代表的功能#define F1 112#define F2 113#define F3 114#define F4 115#define F5 116#define F6 117#define F7 118#define F8 119#define F9 120#define F10 121#define F11 122#define F12 123#define Letter_A 65#define Letter_B 66#define Letter_C 67#define Letter_D 68#define Letter_E 69#define Letter_F 70#define Letter_G 71#define Letter_H 72#define Letter_I 73#define Letter_J 74#define Letter_K 75#define Letter_L 76#define Letter_M 77#define Letter_N 78#define Letter_O 79#define Letter_P 80#define Letter_Q 81#define Letter_R 82#define Letter_S 83#define Letter_T 84#define Letter_U 85#define Letter_V 86#define Letter_W 87#define Letter_X 88#define Letter_Y 89#define Letter_Z 90#define Tab 9#define CapsLk 20#define Wave 192//·~#define Ctrl 162#define Ctrl_R 163#define Alt 164#define Alt_R 165#define Shift 160#define Shift_R 161#define Space 32#define Win 91#define Enter 13#define Esc 27#define Insert 45#define NumLock 144#define PrtSc 44#define Delete 46#define Home 36#define End 35#define PgUp 33#define PgDn 34#define BackSpace 8#define Mouse_RButton 93 //空格键右边第二个键位,有鼠标右键的作用#define Operator_divide 111// /#define Operator_plus 107// +#define Operator_multiply 106// *#define Operator_subtract 109// -#define Decimal_point 110#define Number_0 96#define Number_1 97#define Number_2 98#define Number_3 99#define Number_4 100#define Number_5 101#define Number_6 102#define Number_7 103#define Number_8 104#define Number_9 105#define Left 37#define Right 39#define Up 38#define Down 40//Letter area#define LetterArea_number_0 48// ) #define LetterArea_number_1 49// !#define LetterArea_number_2 50// @#define LetterArea_number_3 51// ##define LetterArea_number_4 52// $#define LetterArea_number_5 53// %#define LetterArea_number_6 54// ^#define LetterArea_number_7 55// &#define LetterArea_number_8 56// *#define LetterArea_number_9 57// (#define subtract 189// -_#define plus_equal 187// + =#define Left_brackets 219// [{#define Right_brackets 221// ]}#define Stop_sign 220// \| #define Semicolon 186// ;:#define Quotation_marks 222// '"#define Comma 188// ,<#define Full_stop 190// .>#define Question_mark 191// ?///加MATHLIBRARY_API后表示外部类class ShortcutKey{public:BYTE key[3];//组合键int index = 0;//组合键键位数量ShortcutKey(BYTE key_1 = 0, BYTE key_2 = 0, BYTE key_3 = 0){this->key[0] = key_1;this->key[1] = key_2;this->key[2] = key_3;}//在组合键中添加一个键位void add(BYTE key_code){if (index < 3){key[index] = key_code;index++;}}//获取组合键中最后一个键位BYTE getkey(){if (index != 0)return key[index - 1];elsereturn 0;}//删除组合键中所有键位void deleteAllkey(){for (int i = 0; i < 3; i++)key[i] = 0;index = 0;}//删除组合键中最后一个键位void deletekey(){if (index>0){key[index - 1] = 0;index--;}}};

键位值是我电脑的键位在全局钩子中对应的数值,其它电脑可能不一样,如果不对自己调一下。

(2)Keyboard.cpp定义

#include "Keyboard.h"//#include <conio.h>#include <windows.h>#include <stdio.h>// 项目的名称,注意这个项目的名称要和最后导出的DLL的文件名一致#define PROJECT_NAME L"Hook"HHOOK g_hHook = NULL;// 全局钩子HWND callframe = NULL;//发送窗口PKBDLLHOOKSTRUCT pKeyboardHookStruct = NULL;int keycode = 0;boolean keydown = false;//按键标识int lastkeycode = 0;//上次按键键值COPYDATASTRUCT cpd;//发送数据结构ShortcutKey Listenkey;//当前监听键// 键盘回调LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam) {if (code < 0 || code == HC_NOREMOVE) {// 如果代码小于零,则挂钩过程必须将消息传递给CallNextHookEx函数,而无需进一步处理,并且应返回CallNextHookEx返回的值。此参数可以是下列值之一。(来自官网手册)return CallNextHookEx(g_hHook, code, wParam, lParam);}//获取键盘值pKeyboardHookStruct = (PKBDLLHOOKSTRUCT)lParam;keycode = pKeyboardHookStruct->vkCode;//键值纠正switch (keycode){case Ctrl_R:keycode = Ctrl;break;case Shift_R:keycode = Shift;break;case Alt_R:keycode = Alt;break;default:break;}//如果按键,判断键值是否是指定键if (pKeyboardHookStruct->flags<128){//如果按下键不是上次的键,组合成组合键,发送当前组合键信息到指定窗口if (lastkeycode != keycode){Listenkey.add(keycode);lastkeycode = keycode;cpd.dwData = 0;//dwData可以是任意值,cpd.cbData = sizeof(ShortcutKey);//指定lpData内存区域的字节数cpd.lpData = &Listenkey;//发送给目录窗口所在进程的数据SendMessage(callframe, WM_COPYDATA, NULL, (LPARAM)&cpd);//发送组合键数据到调用窗口中}}//如果松开键,删除组合键键位if (pKeyboardHookStruct->flags >= 128){//如果松开的是组合键最后的键位,删除最后的键位,否则删除整个组合键if (keycode == lastkeycode){Listenkey.deletekey();lastkeycode = Listenkey.getkey();}else{Listenkey.deleteAllkey();lastkeycode = 0;}}// 将钩子往下传return CallNextHookEx(g_hHook, code, wParam, lParam);}// 安装钩子BOOL InstallHook(HWND frame) {// 【参数1】钩子的类型,这里代表键盘钩子// 【参数2】钩子处理的函数// 【参数3】获取模块,PROJECT_NAME为DLL的项目名称// 【参数4】线程的ID,如果是全局钩子的话,这里要填0,如果是某个线程的钩子,那就需要写线程的IDcallframe = frame;//获取调用窗口HWNDg_hHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardProc, GetModuleHandle(PROJECT_NAME), 0);//WH_KEYBOARD wParam为各键代表值,WH_KEYBOARD_LL不是,而是键的按下和抬起状态,但WH_KEYBOARD时ctrl+space被搜狗输入法拦截,而WH_KEYBOARD_LL不会。if (g_hHook == NULL) {// 钩子安装失败MessageBox(NULL, L"全局钩子1注册失败", L"信息", MB_OK);return FALSE;}return TRUE;}// 卸载钩子,返回是否卸载成功BOOL UninstallHook() {return UnhookWindowsHookEx(g_hHook);}

在回调函数KeyboardProc中将按键组合成组合键,同时通过SendMessage将组合键数据发送给调用窗口。

4.生成Hook.dll

二、MFC调用

1.dll文件导入MFC项目

(1)将Hook.dll文件放在MFC项目的Debug文件夹内(即exe文件所在文件夹)

(2)导入Hook.lib文件和Hook.h文件

注:如果dll代码修改了,需重新生成dll,并将新生成的dll替换Debug文件夹内旧文件。

2.调用dll,通过OnCopyData接收按键信息,并作指定组合键匹配

#include "Hook/Hook/Keyboard.h"ShortcutKey* currentkey;//当前按键ShortcutKey* key1 = new ShortcutKey(Ctrl, Space);//快捷键BOOL CScreenDlg::OnInitDialog(){CDialogEx::OnInitDialog();//安装钩子//AllocConsole();//打开控制台InstallHook(this->m_hWnd);//安装钩子return TRUE; }void CScreenDlg::OnDestroy(){CDialogEx::OnDestroy();// TODO: 在此处添加消息处理程序代码UninstallHook();//卸载钩子//FreeConsole();//关闭控制台}//接收键盘监听dll发送的键盘监听信息BOOL CScreenDlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct){// TODO: 在此添加消息处理程序代码和/或调用默认值//获取键盘监听dll发送的键盘监听信息currentkey = (ShortcutKey*)(pCopyDataStruct->lpData);//判断键位,如果键位和指定组合键匹配,执行指定代码if (currentkey->key[0] == key1->key[0] && currentkey->key[1] == key1->key[1] && currentkey->key[2] == key1->key[2]){// 在此添加键盘消息处理程序代码//this->OnBnClickedButton1();}return CDialogEx::OnCopyData(pWnd, pCopyDataStruct);}

注:

1.安装HOOK钩子,如果调试不需要调试与捕获键盘相关功能时,可注释掉安装钩子代码和OnDestroy中卸载钩子代码。因为安装钩子后,断点调试时,按键盘后断点会卡住整个桌面,要等一段时间才能正常,严重时整个屏幕一直卡着,只能重启电脑。

2.问题在于dll中的回调函数KeyboardProc,只要在KeyboardProc内发送消息或者调用KeyboardProc内使用的变量都会卡,原因不明,网上也没搜到相关解决方案。但这对功能没有影响,就是会影响调试。所以项目调试与钩子无关的功能时,可以注释掉钩子的安装和卸载代码再作调试。

如果觉得《MFC 全局钩子dll注入监听键盘消息》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。