12.2 实现键盘模拟按键
本节将向读者介绍如何使用键盘鼠标操控模拟技术,键盘鼠标操控模拟技术是一种非常实用的技术,可以自动化执行一些重复性的任务,提高工作效率,在Windows系统下,通过使用各种键盘鼠标控制函数实现动态捕捉和模拟特定功能的操作。
键盘鼠标的模拟是实现自动化的必备流程,通常我们可以使用keybd_event()
实现对键盘的击键模拟,使用SetCursorPos()
实现对鼠标的模拟,使用两者的配合读者可以很容易的实现对键盘鼠标的控制,本节将依次封装实现,模拟键盘鼠标控制功能,读者可根据自己的实际需求选用不同的函数片段。
12.2.1 模拟键盘按键
模拟按键的核心功能是通过调用keybd_event()
函数实现的,如下是这段代码的完整实现,首先MySetKeyBig()
函数该函数用于设置键盘状态是否为大小写,用户可以传入一个状态值来设置当前输入法大小写模式,MyAnalogKey()
函数用于实现模拟键盘按键,该函数接收一个英文字符串,并自动实现击键操作,代码实现并不复杂,读者可自行测试功能。
#include <windows.h>
#include <iostream>using namespace std;// 设置键盘大小写状态 为TRUE则切换大写状态,否则切换小写状态
VOID MySetKeyBig(BOOL big = FALSE)
{// 判断键盘CapsLock键是否开启状态,开启状态则为大写,否则为小写if (GetKeyState(VK_CAPITAL)){// 如果当前键盘状态为大写,要求改小写,则模拟按键CapsLock切换状态if (!big){keybd_event(VK_CAPITAL, NULL, KEYEVENTF_EXTENDEDKEY | 0, NULL);keybd_event(VK_CAPITAL, NULL, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, NULL);}std::cout << "[键盘状态] " << "切换大写" << std::endl;}else{// 如果当前键盘状态为小写,要求改大写,则模拟按键CapsLock切换状态if (big){keybd_event(VK_CAPITAL, NULL, KEYEVENTF_EXTENDEDKEY | 0, NULL);keybd_event(VK_CAPITAL, NULL, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, NULL);}std::cout << "[键盘状态] " << "切换小写" << std::endl;}
}// 模拟键盘按键
VOID MyAnalogKey(char* str)
{int iLen = 0;char* tmp = NULL;INPUT* keys = NULL;BOOL bOldState = FALSE;// 保存此操作前的键盘状态bOldState = (BOOL)GetKeyState(VK_CAPITAL);iLen = lstrlen(str);tmp = (char*)malloc(iLen);memmove(tmp, str, iLen);for (int i = 0; i < iLen; i++){// 某些符号非直属键盘按键,这里只过滤转换两个,以后用到其它字符自行添加过滤if (tmp[i] == ' '){// 产生一个击键消息步骤:按下->抬起keybd_event(VK_SPACE, NULL, KEYEVENTF_EXTENDEDKEY | 0, NULL);keybd_event(VK_SPACE, NULL, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, NULL);}else if (tmp[i] == ','){keybd_event(VK_OEM_COMMA, NULL, KEYEVENTF_EXTENDEDKEY | 0, NULL);keybd_event(VK_OEM_COMMA, NULL, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, NULL);}else if (tmp[i] >= 'a' && tmp[i] <= 'z'){// 根据字符大小写切换键盘大小写状态MySetKeyBig(0);// keybd_event只识别大写keybd_event((BYTE)tmp[i] - 32, NULL, KEYEVENTF_EXTENDEDKEY | 0, NULL);keybd_event((BYTE)tmp[i] - 32, NULL, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, NULL);}else if ((tmp[i] >= 'A' && tmp[i] <= 'Z') || (tmp[i] >= '0' && tmp[i] <= '9')){MySetKeyBig(1);keybd_event((BYTE)tmp[i], NULL, KEYEVENTF_EXTENDEDKEY | 0, NULL);keybd_event((BYTE)tmp[i], NULL, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, NULL);}else{keybd_event((BYTE)tmp[i] + 64, NULL, KEYEVENTF_EXTENDEDKEY | 0, NULL);keybd_event((BYTE)tmp[i] + 64, NULL, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, NULL);}Sleep(50);}// 恢复此操作之前的键盘状态MySetKeyBig(bOldState);free(tmp);
}int main(int argc, char *argv[])
{Sleep(5000);MyAnalogKey((char*)"Love life , Love LyShark WelCome LyShark Cpp Home ...");system("pause");return 0;
}
读者可自行编译并运行上述代码片段,将光标移动到记事本中,等待五秒钟,则会依次敲击如下所示的键盘按键;
12.2.2 设置窗体最大化
如下代码实现了设置一个窗体置顶并将该窗体最大化显示的效果,该代码实现原理是通过使用EnumWindows
函数传递一个回调函数,实现对特定窗体的枚举,当找到对应窗体句柄后则将该窗体句柄传递给global_hwnd
全局句柄中,当获取到Google
浏览器句柄之后则通过GetSystemMetrics
函数得到当前全屏窗体的像素比,通过调用SetWindowPos
可将一个窗体设置为置顶显示,最后可调用SendMessage
函数向特定窗体句柄发送最大化消息,使其填充满整个屏幕,代码如下所示;
#include <iostream>
#include <windows.h>using namespace std;HWND global_hwnd = 0;// 将字符串逆序
char * Reverse(char str[])
{int n = strlen(str);int i;char temp;for (i = 0; i < (n / 2); i++){temp = str[i];str[i] = str[n - i - 1];str[n - i - 1] = temp;}return str;
}// 窗体枚举回调函数
BOOL CALLBACK lpEnumFunc(HWND hwnd, LPARAM lParam)
{char WindowName[MAXBYTE] = { 0 };DWORD ThreadId, ProcessId = 0;GetWindowText(hwnd, WindowName, sizeof(WindowName));ThreadId = GetWindowThreadProcessId(hwnd, &ProcessId);printf("句柄: %-9d --> 线程ID: %-6d --> 进程ID: %-6d --> 名称: %s \n", hwnd, ThreadId, ProcessId, WindowName);// 首先逆序输出字符串,然后比较前13个字符if (strncmp(Reverse(WindowName), "emorhC elgooG", 13) == 0){global_hwnd = hwnd;}return TRUE;
}int main(int argc, char* argv[])
{// 枚举Google浏览器句柄EnumWindows(lpEnumFunc, 0);std::cout << "浏览器句柄: " << global_hwnd << std::endl;if (global_hwnd != 0){// 获取系统屏幕宽度与高度 (像素)int cx = GetSystemMetrics(SM_CXSCREEN);int cy = GetSystemMetrics(SM_CYSCREEN);std::cout << "屏幕X: " << cx << " 屏幕Y: " << cy << std::endl;// 传入指定的HWND句柄HWND hForeWnd = GetForegroundWindow();DWORD dwCurID = GetCurrentThreadId();DWORD dwForeID = GetWindowThreadProcessId(hForeWnd, NULL);AttachThreadInput(dwCurID, dwForeID, TRUE);// 将该窗体呼出到顶层ShowWindow(global_hwnd, SW_SHOWNORMAL);SetWindowPos(global_hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);SetWindowPos(global_hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);SetForegroundWindow(global_hwnd);AttachThreadInput(dwCurID, dwForeID, FALSE);// 发送最大化消息SendMessage(global_hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0); // 最大化// SendMessage(global_hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0); // 最小化// SendMessage(global_hwnd, WM_SYSCOMMAND, SC_CLOSE, 0); // 关闭}system("pause");return 0;
}
读者可自行编译并运行上述程序,此时会将谷歌浏览器全屏并置顶显示,输出信息如下图所示;
12.2.3 读取与设置剪辑板
读取与设置剪辑版可用于对数据的拷贝与设置,调用setClipbar
函数并传入一段字符串可实现将传入字符串拷贝到剪辑版的功能,使用getClipBoardValue
则可实现读取剪辑版中的内容到程序内。
#include <iostream>
#include <windows.h>
#include <time.h>// 将字符串写入到剪切板
BOOL setClipbar(const char* data)
{int contentSize = strlen(data) + 1;HGLOBAL hMemory; LPTSTR lpMemory;// 打开剪切板if (!OpenClipboard(NULL)){return FALSE;}// 清空剪切板if (!EmptyClipboard()){return FALSE;}// 对剪切板分配内存if (!(hMemory = GlobalAlloc(GMEM_MOVEABLE, contentSize))){return FALSE;}// 锁定内存区域if (!(lpMemory = (LPTSTR)GlobalLock(hMemory))){return FALSE;}// 复制数据到内存区域,解除内存锁定memcpy_s(lpMemory, contentSize, data, contentSize);GlobalUnlock(hMemory);// 设置剪切板数据if (!SetClipboardData(CF_TEXT, hMemory)){return FALSE;}// std::cout << "已复制: " << data << " 长度: " << contentSize << std::endl;return CloseClipboard();
}// 获取剪切板内容
char* getClipBoardValue()
{// 初始化char* url, *pData;size_t length;// 打开剪切板if (!OpenClipboard(NULL)){return FALSE;}// 获取剪切板内的数据HANDLE hData = GetClipboardData(CF_TEXT);if (hData == NULL){return FALSE;}// 获取数据长度length = GlobalSize(hData);url = (char*)malloc(length + 1);// 将数据转换为字符pData = (char*)GlobalLock(hData);strcpy_s(url, length, pData);// 清理工作url[length] = 0;GlobalUnlock(hData);CloseClipboard();// 返回结果并释放内存char* result = _strdup(url);free(url);return result;
}int main(int argc, char *argv[])
{Sleep(5000);for (size_t i = 0; i < 10; i++){// 填充字符串char MyData[256] = { 0 };sprintf(MyData, "hello lyshark --> %d", i);// 设置到剪辑版BOOL set_flag = setClipbar(MyData);if (set_flag == TRUE){// std::cout << "[*] 已设置" << std::endl;// 获取剪辑版char *data = getClipBoardValue();std::cout << "[剪辑版数据] = " << data << std::endl;}}system("pause");return 0;
}
运行上述程序,依次实现填充字符串并设置到剪辑版,最后再通过getClipBoardValue
函数从剪辑版内读出数据,如下图所示;
本文作者: 王瑞
本文链接: https://www.lyshark.com/post/95b1ad6c.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
相关文章:

12.2 实现键盘模拟按键
本节将向读者介绍如何使用键盘鼠标操控模拟技术,键盘鼠标操控模拟技术是一种非常实用的技术,可以自动化执行一些重复性的任务,提高工作效率,在Windows系统下,通过使用各种键盘鼠标控制函数实现动态捕捉和模拟特定功能的…...

《DevOps 精要:业务视角》- 读书笔记(七)
DevOps 精要:业务视角(七) DevOps历程什么是企业体系的DevOps?DevOps的目标是什么? DevOps的知识体系规范敏捷持续交付IT服务管理以TPS理念为基础 DevOps团队角色流程主管(Process Master)服务主管…...
【随想】每日两题Day.12(实则一题)
题目:15. 三数之和 给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k ,同时还满足 nums[i] nums[j] nums[k] 0 。请 你返回所有和为 0 且不重复的三元组。 注意:答案中不…...

基于复旦微JFM7K325T FPGA的高性能PCIe总线数据预处理载板(100%国产化)
PCIE711是一款基于PCIE总线架构的高性能数据预处理FMC载板,板卡采用复旦微的JFM7K325T FPGA作为实时处理器,实现各个接口之间的互联。该板卡可以实现100%国产化。 板卡具有1个FMC(HPC)接口,1路PCIe x8主机接口&#x…...

什么是原型链(prototype chain)?如何实现继承?
聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 欢迎来到前端入门之旅!感兴趣的可以订阅本专栏哦!这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…...
RabbitMQ 5种工作模式介绍和Springboot具体实现
RabbitMQ有5中工作模式:简单模式、工作队列模式、发布/订阅模式、路由模式和主题模式 简单模式(Simple Mode) 简单模式是最基本的工作模式,也是最简单的模式。在简单模式中,生产者将消息发送到一个队列中,…...

C++ - 可变模版参数 - emplace相关接口函数 - 移动构造函数 和 移动赋值运算符重载 的 默认成员函数
可变模版参数 我们先来了解一下,可变参数。可变参数就是在定义函数的时候,某一个参数位置使用 "..." 的方式来写的,在库当中有一个经典的函数系列就是用的 可变参数:printf()系列就是用的可变参…...

总结三:计算机网络面经
文章目录 1、简述静态路由和动态路由?2、说说有哪些路由协议,都是如何更新的?3、简述域名解析过程,本机如何干预域名解析?4、简述 DNS 查询服务器的基本流程是什么?DNS 劫持是什么?5、简述网关的…...

服务器数据恢复-VMWARE ESX SERVER虚拟机数据恢复案例
服务器数据恢复环境: 几台VMware ESX SERVER共享一台某品牌存储,共有几十组虚拟机。 服务器故障: 虚拟机在工作过程中突然被发现不可用,管理员将设备进行了重启,重启后虚拟机依然不可用,虚拟磁盘丢失&#…...
COCI 2021-2022 #1 - Set 题解
思路 我们将原题中的数的每一位减一,此时问题等价。 下面的异或都是在三进制下的异或。(相当于不进位的加法) 我们考虑原题中的条件,对于每一位,如果相同,则异或值为 0 0 0,如果为 1 1 1&a…...
分享40个极具商业价值的chatGPT提问prompt
原文:分享40个极具商业价值的chatGPT提问prompt | 秋天的童话博客 1、分析并改善定价策略 提示: "分析我当前的[插入产品或服务]定价策略。提出改进建议,并帮助我制定新的定价策略,以最大化利润和客户满意度。" Analyze and Imp…...

一文搞懂到底什么是元宇宙
一、背景 2021年,“元宇宙”是科技界的开端。 2021”元宇宙”这个词在Facebook更名后被点燃了,无疑是21世纪科技界最爆的起点。各式各样的定义、解读都出现了,有人说它是炒作,甚至是骗局,但也有人说它就是互联网的未…...

【重拾C语言】六、批量数据组织(四)线性表—栈和队列
目录 前言 六、批量数据组织——数组 6.1~3 数组基础知识 6.4 线性表——分类与检索 6.5~7 数组初值;字符串、字符数组、字符串数组;类型定义 typedef 6.8 线性表—栈和队列 6.8.1 栈(Stack) 全局变量 isEmpty() isFull…...
力扣刷题-哈希表-一个字符串是否能够由另一个字符串中的字符组成
383 赎金信 给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。 如果可以,返回 true ;否则返回 false 。 magazine 中的每个字符只能在 ransomNote 中使用一次。ransomNote 和 magazin…...
Android使用AOP切面编程
在Android应用程序中,AOP可以被用于许多不同的场景,例如日志记录、权限控制、性能分析等。下面是一个简单的例子,说明如何在Android应用程序中使用AOP切面编程。 首先,我们需要在应用程序中引入AspectJ库。我们可以使用Gradle来完…...

Flutter学习笔记
此篇文章用来记录学习Flutter 和 Dart 相关知识 零.Dart基本数据类型 Dart 是一种静态类型的编程语言,它提供了一系列基本数据类型,用于存储和操作不同种类的数据。以下是 Dart 中的一些基本数据类型以及它们的详细介绍: 1. 整数类型&#…...
软件生命周期中的概念设计和详细设计的主要任务是什么
基础概念 在软件生命周期中,概念设计和详细设计是软件设计阶段中的两个重要环节。 概念设计阶段的主要任务是从业务需求出发,将系统的基本概念、主要功能和关键特性进行抽象和定义。概念设计旨在确定系统的整体架构和关键模块,包括以下主要…...

大数据学习(2)Hadoop-分布式资源计算hive(1)
&&大数据学习&& 🔥系列专栏: 👑哲学语录: 承认自己的无知,乃是开启智慧的大门 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言📝支持一下博>主哦&#x…...
深入探究HTML表单与JavaScript的关系
深入探究HTML表单与JavaScript的关系 引言 HTML表单是网页中数据收集的重要工具,而JavaScript则充当着这些数据的处理者和控制者的角色。二者之间的关系非常紧密,共同构成了现代Web应用中用户交互的基础。在这篇博客中,我们将详细地解析HTM…...

关于Jupyter notebook 创建python3 时进去不能重命名问题及不能编程问题
首先写这篇博客时,已经被这个问题折磨了三天,看了很多博客,其实解决这个问题的关键就是要么没有下pyzmq或者等级太高,要么等级太低,首先我会按照我思路来。 问题如图: 1.自动换行 2.不能重命名 我的解决办…...

业务系统对接大模型的基础方案:架构设计与关键步骤
业务系统对接大模型:架构设计与关键步骤 在当今数字化转型的浪潮中,大语言模型(LLM)已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中,不仅可以优化用户体验,还能为业务决策提供…...

7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...

脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)
一、OpenBCI_GUI 项目概述 (一)项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台,其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言,首次接触 OpenBCI 设备时,往…...
【Elasticsearch】Elasticsearch 在大数据生态圈的地位 实践经验
Elasticsearch 在大数据生态圈的地位 & 实践经验 1.Elasticsearch 的优势1.1 Elasticsearch 解决的核心问题1.1.1 传统方案的短板1.1.2 Elasticsearch 的解决方案 1.2 与大数据组件的对比优势1.3 关键优势技术支撑1.4 Elasticsearch 的竞品1.4.1 全文搜索领域1.4.2 日志分析…...

实战设计模式之模板方法模式
概述 模板方法模式定义了一个操作中的算法骨架,并将某些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的前提下,重新定义算法中的某些步骤。简单来说,就是在一个方法中定义了要执行的步骤顺序或算法框架,但允许子类…...
WEB3全栈开发——面试专业技能点P7前端与链上集成
一、Next.js技术栈 ✅ 概念介绍 Next.js 是一个基于 React 的 服务端渲染(SSR)与静态网站生成(SSG) 框架,由 Vercel 开发。它简化了构建生产级 React 应用的过程,并内置了很多特性: ✅ 文件系…...

6.9-QT模拟计算器
源码: 头文件: widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QMouseEvent>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent nullptr);…...
2025年低延迟业务DDoS防护全攻略:高可用架构与实战方案
一、延迟敏感行业面临的DDoS攻击新挑战 2025年,金融交易、实时竞技游戏、工业物联网等低延迟业务成为DDoS攻击的首要目标。攻击呈现三大特征: AI驱动的自适应攻击:攻击流量模拟真实用户行为,差异率低至0.5%,传统规则引…...

2025年- H71-Lc179--39.组合总和(回溯,组合)--Java版
1.题目描述 2.思路 当前的元素可以重复使用。 (1)确定回溯算法函数的参数和返回值(一般是void类型) (2)因为是用递归实现的,所以我们要确定终止条件 (3)单层搜索逻辑 二…...