【小沐学C++】C++ 实现鼠标键盘钩子HOOK

文章目录

  • 1、简介
  • 2、相关函数
    • 2.1 SetWindowsHookEx
    • 2.2 UnhookWindowsHookEx
    • 2.3 CallNextHookEx
  • 3、相关结构体
    • 3.1 KBDLLHOOKSTRUCT
    • 3.2 MSLLHOOKSTRUCT
  • 4、挂钩过程
  • 5、代码测试
    • 5.1 代码1
  • 结语

1、简介

https://learn.microsoft.com/zh-cn/windows/win32/winmsg/about-hooks

挂钩是应用程序截获消息、鼠标操作和击键等事件的机制。 截获特定类型的事件的函数称为 挂钩过程。 挂钩过程可以对其接收的每个事件执行操作,然后修改或放弃该事件。
在这里插入图片描述

挂钩是系统消息处理机制中的一个点,其中应用程序可以安装子例程来监视系统中的消息流量,并在某些类型的消息到达目标窗口过程之前对其进行处理。
在这里插入图片描述

以下一些示例使用 挂钩:

  • 监视用于调试的消息
  • 支持录制和播放宏
  • 为帮助密钥 (F1) 提供支持
  • 模拟鼠标和键盘输入
  • 实现基于计算机的训练 (CBT) 应用程序

在这里插入图片描述

2、相关函数

2.1 SetWindowsHookEx

将应用程序定义的挂钩过程安装到挂钩链中。 你将安装挂钩过程来监视系统的某些类型的事件。 这些事件与特定线程或与调用线程位于同一桌面中的所有线程相关联。

HHOOK SetWindowsHookExA([in] int       idHook,[in] HOOKPROC  lpfn,[in] HINSTANCE hmod,[in] DWORD     dwThreadId
);HHOOK SetWindowsHookExW([in] int       idHook,[in] HOOKPROC  lpfn,[in] HINSTANCE hmod,[in] DWORD     dwThreadId
);
  • WH_KEYBOARD
    安装用于监视击键消息的挂钩过程。 有关详细信息,请参阅 KeyboardProc 挂钩过程。
LRESULT CALLBACK KeyboardProc(_In_ int    code,_In_ WPARAM wParam,_In_ LPARAM lParam
);
  • WH_KEYBOARD_LL
    安装用于监视低级别键盘输入事件的挂钩过程。 有关详细信息,请参阅 [LowLevelKeyboardProc] (/windows/win32/winmsg/lowlevelkeyboardproc) 挂钩过程。
LRESULT CALLBACK LowLevelKeyboardProc(_In_ int    nCode,_In_ WPARAM wParam,_In_ LPARAM lParam
);
// 其中lParam指向 KBDLLHOOKSTRUCT 结构的指针。
  • WH_MOUSE
    安装监视鼠标消息的挂钩过程。 有关详细信息,请参阅 MouseProc 挂钩过程。
LRESULT CALLBACK MouseProc(_In_ int    nCode,_In_ WPARAM wParam,_In_ LPARAM lParam
);
  • WH_MOUSE_LL
    安装用于监视低级别鼠标输入事件的挂钩过程。 有关详细信息,请参阅 LowLevelMouseProc 挂钩过程。
LRESULT CALLBACK LowLevelMouseProc(_In_ int    nCode,_In_ WPARAM wParam,_In_ LPARAM lParam
);
// 其中lParam指向 MSLLHOOKSTRUCT 结构的指针。

2.2 UnhookWindowsHookEx

删除 SetWindowsHookEx 函数安装在挂钩链中的挂钩过程。

BOOL UnhookWindowsHookEx([in] HHOOK hhk
);

2.3 CallNextHookEx

将挂钩信息传递给当前挂钩链中的下一个挂钩过程。 挂钩过程可以在处理挂钩信息之前或之后调用此函数。

LRESULT CallNextHookEx([in, optional] HHOOK  hhk,[in]           int    nCode,[in]           WPARAM wParam,[in]           LPARAM lParam
);

3、相关结构体

3.1 KBDLLHOOKSTRUCT

包含有关低级别键盘输入事件的信息。

typedef struct tagKBDLLHOOKSTRUCT {DWORD     vkCode;DWORD     scanCode;DWORD     flags;DWORD     time;ULONG_PTR dwExtraInfo;
} KBDLLHOOKSTRUCT, *LPKBDLLHOOKSTRUCT, *PKBDLLHOOKSTRUCT;

3.2 MSLLHOOKSTRUCT

包含有关低级别鼠标输入事件的信息。

typedef struct tagMSLLHOOKSTRUCT {POINT     pt;DWORD     mouseData;DWORD     flags;DWORD     time;ULONG_PTR dwExtraInfo;
} MSLLHOOKSTRUCT, *LPMSLLHOOKSTRUCT, *PMSLLHOOKSTRUCT;

4、挂钩过程

  • 定义挂钩过程
    为了利用特定类型的挂钩,开发人员提供了一个挂钩过程,并使用 SetWindowsHookEx 函数将其安装到与挂钩关联的链中。 挂钩过程必须具有以下语法:
LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam
)
{// process event...return CallNextHookEx(NULL, nCode, wParam, lParam);
}

SetWindowsHookEx 函数始终在挂钩链的开头安装挂钩过程。 当发生由特定类型的挂钩监视的事件时,系统会在与挂钩关联的挂钩链的开头调用过程。 链中的每个挂钩过程确定是否将事件传递给下一个过程。 挂钩过程通过调用 CallNextHookEx 函数将事件传递给下一过程。

  • 安装和释放挂钩过程
    SetWindowsHookEx 传递模块句柄、指向挂钩过程入口点的指针,以及线程标识符的 0,指示挂钩过程应与调用线程位于同一桌面中的所有线程相关联。 以下示例中显示了此序列。
HOOKPROC hkprcSysMsg;
static HINSTANCE hinstDLL; 
static HHOOK hhookSysMsg; hinstDLL = LoadLibrary(TEXT("c:\\myapp\\sysmsg.dll")); 
hkprcSysMsg = (HOOKPROC)GetProcAddress(hinstDLL, "SysMessageProc"); hhookSysMsg = SetWindowsHookEx( WH_SYSMSGFILTER,hkprcSysMsg,hinstDLL,0);

可以通过调用 UnhookWindowsHookEx 函数来释放特定于线程的挂钩过程, (从挂钩链) 中删除其地址,并指定要释放的挂钩过程的句柄。 一旦应用程序不再需要挂钩过程,就立即释放它。

5、代码测试

5.1 代码1


#include "pch.h"
#include <Windows.h>
#include <iostream>std::string get_time()
{SYSTEMTIME sys;GetLocalTime(&sys);char time[255] = { 0 };sprintf_s(time, "[%4d/%02d/%02d %02d:%02d:%02d]", sys.wYear, sys.wMonth, sys.wDay, sys.wHour, sys.wMinute, sys.wSecond);return std::string(time);
}LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{/*typedef struct tagKBDLLHOOKSTRUCT {DWORD     vkCode;		// 按键代号DWORD     scanCode;		// 硬件扫描代号,同 vkCode 也可以作为按键的代号。DWORD     flags;		// 事件类型,一般按键按下为 0 抬起为 128。DWORD     time;			// 消息时间戳ULONG_PTR dwExtraInfo;	// 消息附加信息,一般为 0。}KBDLLHOOKSTRUCT,*LPKBDLLHOOKSTRUCT,*PKBDLLHOOKSTRUCT;*/KBDLLHOOKSTRUCT* ks = (KBDLLHOOKSTRUCT*)lParam;		// 包含低级键盘输入事件信息char buffer[255];DWORD code = ks->vkCode;std::string t = get_time();char state[20];if (wParam == WM_KEYDOWN){strcpy_s(state, "按下");}else if (wParam == WM_KEYUP){strcpy_s(state, "抬起");}sprintf_s(buffer, "[键盘]%s 键代码:%d %s", t.c_str(), code, state);std::cout << buffer << std::endl;//return 1;	// 拦截消息return CallNextHookEx(NULL, nCode, wParam, lParam);
}LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{/*typedef struct tagMOUSEHOOKSTRUCT {POINT   pt;					// Point数据HWND    hwnd;				// 接收鼠标消息的窗体的句柄UINT    wHitTestCode;		// 指定点击测试值ULONG_PTR dwExtraInfo;		// 指定和该消息相关联的附加信息。} MOUSEHOOKSTRUCT, FAR* LPMOUSEHOOKSTRUCT, * PMOUSEHOOKSTRUCT;*/MOUSEHOOKSTRUCT* ms = (MOUSEHOOKSTRUCT*)lParam;POINT pt = ms->pt;std::string time = get_time();char buffer[1024];char state[20] = "未知";if (wParam == WM_LBUTTONDOWN){strcpy_s(state, "左键按下");}else if (wParam == WM_LBUTTONUP){strcpy_s(state, "左键抬起");}else if (wParam == WM_RBUTTONDOWN){strcpy_s(state, "右键按下");}else if (wParam == WM_RBUTTONUP){strcpy_s(state, "右键抬起");}else if (wParam == WM_MOUSEMOVE){strcpy_s(state, "移动");}sprintf_s(buffer, "[鼠标]%s 键代码:x:%d y:%d %s", time.c_str(), pt.x, pt.y, state);std::cout << buffer << std::endl;//return 1;	// 拦截消息return CallNextHookEx(NULL, nCode, wParam, lParam);}int main()
{HINSTANCE hM = GetModuleHandle(NULL), hK = GetModuleHandle(NULL);HHOOK g_Hook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardProc, hK, 0);HHOOK g_Hook2 = SetWindowsHookEx(WH_MOUSE_LL, MouseProc, hM, 0);// 消息循环MSG msg;while (GetMessage(&msg, NULL, 0, 0)){TranslateMessage(&msg);DispatchMessage(&msg);}UnhookWindowsHookEx(g_Hook);return 0;
}

在这里插入图片描述

结语

如果您觉得该方法或代码有一点点用处,可以给作者点个赞,或打赏杯咖啡;╮( ̄▽ ̄)╭
如果您感觉方法或代码不咋地//(ㄒoㄒ)//,就在评论处留言,作者继续改进;o_O???
如果您需要相关功能的代码定制化开发,可以留言私信作者;(✿◡‿◡)
感谢各位大佬童鞋们的支持!( ´ ▽´ )ノ ( ´ ▽´)っ!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/603395.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java学习笔记(十)——异常

一、异常的概念 二、异常体系图&#xff08;重要&#xff09; 三、常见的异常 &#xff08;一&#xff09;常见的运行时异常 1、NullPointerException空指针异常 2、ArithmeticException数学运算异常 3、ArrayIndexOutOfBoundsException数组下标越界异常 4、ClassCastEx…

CSS 压重按钮 效果

<template><view class="cont"><div class="container"><div class="pane"><!-- 选项1 --><label class="label" @click="handleOptionClick(0)":style="{ color: selectedOption ==…

约数个数和约数之和算法总结

知识概览 约数个数 由算数基本定理 可得对于N的任何一个约数d&#xff0c;有 因为N的每一个约数和~的一种选法是一一对应的&#xff0c;根据乘法原理可得&#xff0c; 一个数的约数个数为 约数之和 一个数的约数之和公式为 多项式乘积的每一项为 正好对应的是一个数的每一个约…

【网络安全】upload靶场pass11-17思路

目录 Pass-11 Pass-12 Pass-13 Pass-14 Pass-15 Pass-16 Pass-17 &#x1f308;嗨&#xff01;我是Filotimo__&#x1f308;。很高兴与大家相识&#xff0c;希望我的博客能对你有所帮助。 &#x1f4a1;本文由Filotimo__✍️原创&#xff0c;首发于CSDN&#x1f4da;。 &#x…

JetCache源码解析——配置加载和初始化

JetCache自动化配置加载 JetCache的配置加载主要是在jetcache-autoconfigure模块中完成的&#xff0c;其中加载配置的核心类是JetCacheAutoConfiguration&#xff0c;主要是用于创建全局性的一些Bean&#xff0c;例如全局缓存配置类GlobalCacheConfig&#xff0c;AutoConfigur…

数据密集型应用系统设计--第2章 数据模型与查询语言

一、引言 数据模型可能是开发软件最重要的部分,而且还对如何思考待解决的问题都有深远的影响。 大多数应用程序是通过一层一层叠加数据模型来构建的。每一层都面临的关键问题是&#xff1a;如何将其用下一层来表示&#xff1f; 1.作为一名应用程序开发人员&#xff0c;观测现实…

yarn无法加载文件和‘vue-cli-service‘ 不是内部或外部命令解决方法

导致此错误的原因是&#xff0c;PowerShell 执行策略&#xff0c;默认设置为Restricted不加载配置文件或运行脚本。需变更设置为RemoteSigned&#xff0c;变更过程为&#xff1a; 1.运行 Windows PowerShell&#xff08;管理员&#xff09;&#xff0c;执行命令set-ExecutionPo…

使用redis时快速考虑的问题

使用场景 ap组件程序是否容忍极限丢失1s数据是否可以不依赖redis就能实现是否过度依赖redis 数据结构 5种结构选择不同结构有自己的限制&#xff0c;使用前需考虑限制考虑当前业务最适合那种解构&#xff0c;或多种解构混合使用 key设计 大keykey的格式热key敏感数据 过…

Redis的IO多路复用原理解析

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring源码、JUC源码、Kafka原理、分布式技术原理、数据库技术&#x1f525;如果感觉博主的文章还不错的…

SpringCloud系列篇:入门讲解Spring Cloud是什么

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于SpringCloud的相关操作吧 目录 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 一.Spring Cloud是什么 二.Spring …

通俗易懂的15个Java Lambda表达式案例

文章目录 1. **实现Runnable接口**&#xff1a;2. **事件监听器**&#xff08;如Swing中的ActionListener&#xff09;&#xff1a;3. **集合遍历**&#xff08;使用forEach方法&#xff09;&#xff1a;4. **过滤集合**&#xff08;使用Stream API&#xff09;&#xff1a;5. …

硬盘结构损坏且无法读取恢复方法

硬盘结构损坏且无法读取是计算机存储设备的一种常见故障。当硬盘出现此类问题时&#xff0c;用户往往无法正常访问存储在硬盘中的数据。本文将深入分析硬盘结构损坏且无法读取的潜在原因&#xff0c;并探讨有效的解决方法&#xff0c;以帮助用户恢复数据和正常使用硬盘。 硬盘结…

Vue3 结合typescript 组合式函数(1)

在App.vue文件中 实现鼠标点击文件&#xff0c;显示坐标值 第一种方法 第二种方法&#xff1a;组合式函数 结果&#xff1a; 官网推荐组合函数&#xff1a;https://vueuse.org

进阶学习——Linux系统——程序和进程

目录 一、程序和进程的关系 1.程序 2.进程 2.1线程 2.2协程 3.进程与线程的区别 4.总结 4.1延伸 5.进程使用内存的问题 5.1内存泄漏——Memory Leak 5.2内存溢出——Memory Overflow 5.3内存不足——OOM&#xff08;out of memory&#xff09; 5.4进程使用内存出现…

vue3 里的 ts 类型工具函数

目录 前言一、PropType\<T>二、MaybeRef\<T>三、MaybeRefOrGetter\<T>四、ExtractPropTypes\<T>五、ExtractPublicPropTypes\<T>六、ComponentCustomProperties七、ComponentCustomOptions八、ComponentCustomProps九、CSSProperties 前言 相关 …

报告解读:中国新一代终端安全市场洞察,2023

报告解读 中国新一代终端安全市场洞察 2023 安全防御的“最前线” 01 混沌的企业安全 以下来自CSO们最关注的安全热点问题&#xff1a; Q1我们如何看待当下泛化的终端安全&#xff0c;混合的IT环境企业面临的安全变化&#xff1f; IDC&#xff1a;伴随着全球数字化转型的快…

山西电力市场日前价格预测【2024-01-08】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2024-01-08&#xff09;山西电力市场全天平均日前电价为247.82元/MWh。其中&#xff0c;最高日前电价为373.22元/MWh&#xff0c;预计出现在18:00。最低日前电价为0.00元/MWh&#xff0c;预计出…

eureka工作原理是什么

EUREKA 是一个基于 RESTful 风格的服务发现系统&#xff0c;它主要用于帮助实现在微服务架构中的服务自动发现与注册。其工作原理主要包括以下几个步骤&#xff1a; 注册中心&#xff1a;EUREKA 中有一个集中的注册中心&#xff0c;所有的服务都将在此注册和发现。注册中心可以…

CentOS中开启mysql挂载

挂载的作用其实说白了就是备份。防止数据库文件损害或者数据库被误删导致数据丢失。 创建一个文件名为my.cnf内容如下 # Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. # # This program is free software; you can redistribute it and/or modif…

数据结构第九弹---循环队列

循环队列 1、循环队列的定义2、循环队列的结构3、循环队列的实现3.1、初始化队列3.2、判断是否为空3.3、判断是否为满3.4、入队3.5、出队3.6、返回队头元素3.7、返回队尾元素3.8、销毁队列 4、代码汇总总结 1、循环队列的定义 顺序队列在使用过程中容易出现虚假的满状态&#x…