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 许可协议。转载请注明出处!

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

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

相关文章

数字孪生和数据分析:数字化时代的力量结合

在当今数字化时代&#xff0c;数据是无处不在的。企业、政府和个人不仅生成了大量数据&#xff0c;还寻求从中获取有价值的信息以进行更好的决策。在这个背景下&#xff0c;数字孪生和数据分析成为了迎合这一需求的两个关键概念。本文带大家一起探讨二者之间相辅相成的关系。 一…

Spring Boot:自定义注解--annotation

目录 自定义注解的定义和作用范围如何创建自定义注解创建注解接口 如何使用自定义注解进行数据验证创建注解处理器控制器中使用注解 如何为字段添加注解 自定义注解的定义和作用范围 自定义注解可以作用在类、方法、属性、参数、异常、字段或其他注解上。 如何创建自定义注解…

Google AdSense 账户开通网站广告位后如何配置付款电汇账号的详细教程!

本篇文章主要讲解&#xff1a;Google AdSense 账户开通网站广告位后如何配置付款电汇账号的详细教程。通过本文章可以快速了解开通账户配置权限的整体流程&#xff0c;很多小白朋友注册完毕后发现根本没有配置账号的入口&#xff0c;这篇文章能够告诉你详细的原有。 日期&#…

知识增强语言模型提示 零样本知识图谱问答10.8+10.11

知识增强语言模型提示 零样本知识图谱问答 摘要介绍相关工作方法零样本QA的LM提示知识增强的LM提示与知识问题相关的知识检索 实验设置数据集大型语言模型基线模型和KAPIN评估指标实现细节 实验结果和分析结论 摘要 大型语言模型&#xff08;LLM&#xff09;能够执行 零样本cl…

CAN和CANFD通信介绍

CAN&#xff08;Controller Area Network&#xff0c;控制器局域网&#xff09;是一种串行通信技术&#xff0c;专门用于在汽车电子控制单元&#xff08;ECU&#xff09;之间实现可靠的数据交换。 CAN协议介绍 电子化 汽车近年来的发展呈现出以电子化为主的特点。电子化的主…

Lab 1: Unix utilities汇总

这个实验主要学习了常用的一些系统调用。 Lab 1: Unix utilities Boot xv6 (easy) git克隆&#xff0c;切换分支&#xff0c;qemu。根据要求进行操作即可。 $ git clone git://g.csail.mit.edu/xv6-labs-2020 $ cd xv6-labs-2020 $ git checkout util $ make qemusleep (ea…

数据中心供配电及能效管理系统的设计要点

摘要&#xff1a;现代的数据中心中都包括大量的计算机&#xff0c;对于这种场所的电力供应&#xff0c;都要求供电系统需要在所有的时间都有效&#xff0c;这就不同于一般建筑的供配电系统&#xff0c;它是一个交叉的系统&#xff0c;涉及到市电供电、防雷接地、防静电、UPS不间…

分类预测 | MATLAB实现KOA-CNN-BiGRU开普勒算法优化卷积双向门控循环单元数据分类预测

分类预测 | MATLAB实现KOA-CNN-BiGRU开普勒算法优化卷积双向门控循环单元数据分类预测 目录 分类预测 | MATLAB实现KOA-CNN-BiGRU开普勒算法优化卷积双向门控循环单元数据分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.MATLAB实现KOA-CNN-BiGRU开普勒算法优化…

Visual Studio 2022新建项目时没有ASP.NET项目

一、Visual Studio 2022新建项目时没有ASP.NET项目 1、打开VS开发工具&#xff0c;选择工具菜单&#xff0c;点击“获取工具和功能” 2、选择“ASP.NET和Web开发”和把其他项目模板&#xff08;早期版本&#xff09;勾选上安装即可

ICPC 2019-2020 North-Western Russia Regional Contest

A (codeforces.com) 这题在移动不被挡板挡住以及不超过边界的情况下&#xff0c;每次走的越多那么次数就越少 只要两个每次都走b-a步&#xff08;已经是不被挡板挡住走的最多了&#xff09;&#xff0c;就不用考虑被挡板挡住的情况&#xff0c;只用单独考虑了&#xff0c;如果…

2023年陕西省安全员B证证考试题库及陕西省安全员B证试题解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年陕西省安全员B证证考试题库及陕西省安全员B证试题解析是安全生产模拟考试一点通结合&#xff08;安监局&#xff09;特种作业人员操作证考试大纲和&#xff08;质检局&#xff09;特种设备作业人员上岗证考试大…

ArcGIS/GeoScene脚本:基于粒子群优化的支持向量机分类模型

参数输入 输出 栅格 预测为负类的概率 预测为正类的概率 二值化结果 评估结果 ROC曲线

kepler.gl 笔记:Layer

Point S2Layer Icon Line Cluster Polygon 一个路径型的GeoJSON图层可以显示诸如行程路线或等值线的数据。轮廓的颜色可以使用数值字段进行设置。 一个多边形型的GeoJSON图层本质上是一种区域填充图层&#xff0c;最适合用于呈现地理围栏。填充颜色或高度可以使用数值字…

LLVM(5)ORC实例分析

ORC实例总结 总结 因为API茫茫多&#xff0c;逻辑上的一些概念需要搞清&#xff0c;编码时会容易很多。JIT的运行实体使用LLVMOrcCreateLLJIT可以创建出来&#xff0c;逻辑上的JIT实例。JIT实例需要加入运行库&#xff08;依赖库&#xff09;和用户定义的context&#xff08;…

设计模式 - 状态模式

目录 一. 前言 二. 实现 一. 前言 状态模式&#xff08;State Pattern&#xff09;&#xff1a;它主要用来解决对象在多种状态转换时&#xff0c;需要对外输出不同的行为的问题。状态和行为是一一对应的&#xff0c;状态之间可以相互转换。当一个对象的内在状态改变时&#x…

点击、拖曳,15分钟搞定BI零售数据分析

早几年做数据分析还很依赖IT&#xff0c;过程复杂、耗时长、灵活性差&#xff0c;但这几年随着BI智能数据分析技术的成长&#xff0c;零售数据分析发生了翻天覆地的变化&#xff0c;其中最直观的一点就是&#xff1a;点击、拖曳&#xff0c;15分钟内就能搞定BI零售数据分析。 …

批量给每一段文字 段落加上符号

批量给每一段文字 段落加上符号 例如 要给下面的内容统一加上[xxxxxxxx] 查找替换–>查找内容 &#xff1a;输入前面统一的标识 替换为&#xff1a;–>特殊格式–>查找内容–>全部替换 先在前面加符号 在后面加符号&#xff1a; 最后效果&#xff1a;

QTableWidget 表格增删数据

QTableWidgetQTableWidgetQTableWidget部分使用方法&#xff0c;如在表格中插入或删除一行数据以及清空表格数据等。在添加数据时&#xff0c;设置了条件判断如正则表达式&#xff0c;若用户输入的数据不合法&#xff0c;则添加失败并提示用户错误的地方&#xff0c;便于用户修…

3.3 使用广播信道的数据链路层

思维导图&#xff1a; 3.3.1 局域网的数据链路层 ### 3.3 使用广播信道的数据链路层 #### 简介 - 广播信道支持一对多通信。 - 局域网技术在20世纪70年代末兴起&#xff0c;现在在计算机网络中占有主导地位。 #### 3.3.1 局域网的数据链路层 **局域网的特点&#xff1a;** 1…

9-AJAX-上-原理详解

一、定义 1、什么是Ajax Ajax&#xff1a;即异步 JavaScript 和XML。Ajax是一种用于创建快速动态网页的技术。通过在后台与进行少量数据交换&#xff0c;Ajax可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下&#xff0c;对网页的某部分进行更新。而传统的…