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  17
BYTE 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  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// .>//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,如果是某个线程的钩子,那就需要写线程的ID  callframe = 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注入监听键盘消息相关推荐

  1. 基于低级键盘钩子的dota改键(全局+免DLL注入)MFC实现(源码+总结)

    上一篇文章已经写了基于 普通键盘钩子(单线程+DLL)来实现dota改键.http://blog.csdn.net/a576323437/article/details/8037138 这一次,基于 ...

  2. 全局钩子(hook鼠标键盘消息)

    生成DLL文件,模块定义文件为: LIBRARY Hook EXPORTS SetHook @2 实现文件如下.其中g_hWnd为所有进程共享,并且绕过了系统对可写数据的写时复制机制,维护的是一份拷贝 ...

  3. 【旧文章搬运】Windows中全局钩子DLL的加载过程

    原文发表于百度空间,2011-03-24 ========================================================================== 看雪上别 ...

  4. 【逆向】【Part 3】DLL注入

    目录 一.通过自制调试器来理解其原理 1.调试器的工作原理 实现反汇编功能(重点) 重点分析exception_debug_event 重点:1.对调试器程序增加异常处理操作功能,核心API, CON ...

  5. 关于dll注入方式的学习(全局钩子注入)

    何为dll注入 DLL注入技术,一般来讲是向一个正在运行的进程插入/注入代码的过程.我们注入的代码以动态链接库(DLL)的形式存在.DLL文件在运行时将按需加载(类似于UNIX系统中的共享库(shar ...

  6. 【C++】代码实现:全局钩子注入技术

    一.概述 在 Windows  中大部分的应用程序都是基于消息机制的,它们都有一个过程函数,根据不同的消息完成不同的功能. Windows 操作系统提供的钩子机制就是用来截获和监视系统中这些消息的. ...

  7. 如何在Revit中监听键盘事件

    欢迎加入BIM行业开发交流1群 群号:711844216(满),二群群号:1016453207 背景 小伙伴们在做revit二次开发的时候,可能会需要在自己做的插件运行时,去监听某个按键然后做出相应的 ...

  8. 【安全技术】关于几种dll注入方式的学习

    何为dll注入 DLL注入技术,一般来讲是向一个正在运行的进程插入/注入代码的过程.我们注入的代码以动态链接库(DLL)的形式存在.DLL文件在运行时将按需加载(类似于UNIX系统中的共享库(shar ...

  9. Windows 全局钩子 Hook 详解

    监控程序的实现       我们发现一些木马或其他病毒程序常常会将我们的键盘或鼠标的操作消息记录下来然后再将它发到他们指定的地方以实现监听.这种功能其他是利用了全局钩子将鼠标或键盘消息进行了截取,从而 ...

最新文章

  1. ssl *** 思科支持设备型号
  2. WCF duplex service + silverlight 聊天代码
  3. 窗口的z-order是什么?PyQt5
  4. centos7不能安装mysql数据库_Centos7 上安装mysql遇上的问题:mysql无法正常启动-Go语言中文社区...
  5. 笔记-高项案例题-2019年上-采购管理
  6. CSS3中的变形处理
  7. mongodb复制集部署
  8. Android开发中调用Spring CXF整合发布的WebService接口为什么抛出异常错误?
  9. Linux 变量和结构体
  10. 可靠消息服务在支付领域的应用
  11. JS实现,页面显示数据加载,显示加载效果,加载完成显示数据
  12. 什么是servlet?servlet有什么用?
  13. Spring定时器@Scheduled
  14. 计算机二级考试科目vfp,计算机二级考试科目及内容
  15. ThinkPad S2 安装deepin系统,安装rtl8821ce无线网卡驱动,适合deepin/ubuntu
  16. To prevent a memory leak, the JDBC Driver has been forcibly unregistered. 解决方法
  17. Android 项目上线流程总结
  18. Spring Boot自定义starter
  19. PCB板的绘制原来是这样完成的——布线
  20. 深度学习7日入门-CV疫情特辑心得

热门文章

  1. mybatis 绑定失败:Invalid bound statement (not found): com.demo.service.api.dao.SysUserMapper.insert
  2. 导出Excel—外部表不是预期的格式
  3. 使用线性回归构建房价预测模型
  4. 码元速率估计-速率信号法
  5. escape JavaScript
  6. 浅谈JS中的escape,URLEncode与encodeURIComponent
  7. python包发布到Pypi官网
  8. ftp服务器上传不了文件怎么办,ftp服务器怎么上传不了文件
  9. 语音识别:利用百度智能进行语音识别
  10. 保护你的 Flutter 应用程序