C++:模拟键盘鼠标(附带源码)

一、项目背景详细介绍

在实际工程中,我们经常需要让程序像“人”一样操作电脑

  • 自动化测试:点击按钮、输入文本
  • 运维/工具:批量操作 GUI 程序
  • 辅助工具:快捷操作、脚本化流程
  • 教学实验:理解 OS 输入事件链路

这些需求的核心,都是:

程序如何向操作系统“注入”键盘与鼠标输入事件?

1. Windows 输入系统的基本认知

Windows 中,键盘和鼠标并不是直接“给应用”的,而是:

硬件 → 驱动 → 系统输入队列 → 消息分发 → 窗口

只要我们向系统输入队列提交合法事件,系统就会像处理真实硬件一样处理它。


2. 常见实现方式对比

方式特点是否推荐
keybd_event / mouse_event老 API❌ 已过时
SendInput官方推荐
驱动层复杂/高风险
注入 DLL非必要

本文只使用官方推荐的 SendInput


3. 本项目目标

实现一个教学 + 工程可用的输入模拟模块,支持:

  • 键盘按下 / 抬起
  • 鼠标移动
  • 鼠标点击(左 / 右)
  • 简单组合输入
  • 清晰、可复用的封装

二、项目需求详细介绍


1. 功能需求

实现以下能力:

  1. 模拟单个键按下与释放
  2. 模拟字符串输入
  3. 模拟鼠标移动到屏幕坐标
  4. 模拟鼠标左键 / 右键点击
  5. 支持组合示例(键盘 + 鼠标)

2. 技术约束

  • 平台:Windows
  • 语言:C++(C++11+)
  • API:SendInput
  • 不依赖第三方库

3. 合规与边界说明

  • 仅用于自动化、测试、辅助工具
  • 不包含隐藏、绕过、防检测等行为
  • 不针对任何未授权系统

三、相关技术详细介绍


1. SendInput 是什么?

SendInput 是 Windows 提供的统一输入注入接口

UINT SendInput( UINT cInputs, LPINPUT pInputs, int cbSize );

它可以一次性提交多个输入事件,系统会按顺序处理。


2. INPUT 结构体分类

INPUT { type: INPUT_KEYBOARD INPUT_MOUSE }

  • 键盘:KEYBDINPUT
  • 鼠标:MOUSEINPUT

3. 键盘扫描码 vs 虚拟键码

  • 虚拟键码(VK):更常用、更直观
  • 扫描码:更底层(本文不展开)

4. 屏幕坐标说明

SendInput绝对坐标范围是:

X: 0 ~ 65535 Y: 0 ~ 65535

需要从屏幕像素转换。


四、实现思路详细介绍


1. 模块设计

封装一个 InputSimulator,提供:

  • KeyDown / KeyUp
  • SendText
  • MouseMove
  • MouseClick

2. 核心思想

构造 INPUT 结构 → 调用 SendInput → 系统分发

3. 工程可扩展性

后续可扩展:

  • 热键(Ctrl + C)
  • 拖拽
  • 滚轮
  • 多屏支持

五、完整实现代码

/************************************************************ * 文件:InputSimulator.h * 描述:键盘与鼠标模拟工具(SendInput) ************************************************************/ #ifndef INPUT_SIMULATOR_H #define INPUT_SIMULATOR_H #include <windows.h> #include <string> /** * @brief 输入模拟器 */ class InputSimulator { public: /** * @brief 模拟按键按下 */ static void KeyDown(WORD vk) { INPUT input{}; input.type = INPUT_KEYBOARD; input.ki.wVk = vk; input.ki.dwFlags = 0; // 按下 SendInput(1, &input, sizeof(INPUT)); } /** * @brief 模拟按键释放 */ static void KeyUp(WORD vk) { INPUT input{}; input.type = INPUT_KEYBOARD; input.ki.wVk = vk; input.ki.dwFlags = KEYEVENTF_KEYUP; SendInput(1, &input, sizeof(INPUT)); } /** * @brief 发送一个按键(按下 + 抬起) */ static void KeyPress(WORD vk) { KeyDown(vk); Sleep(10); KeyUp(vk); } /** * @brief 模拟输入字符串(仅示例英文/ASCII) */ static void SendText(const std::string& text) { for (char c : text) { SHORT vk = VkKeyScanA(c); if (vk == -1) continue; bool shift = (vk & 0x0100) != 0; WORD vkCode = vk & 0xFF; if (shift) KeyDown(VK_SHIFT); KeyPress(vkCode); if (shift) KeyUp(VK_SHIFT); Sleep(5); } } /** * @brief 将屏幕像素坐标转换为 SendInput 绝对坐标 */ static LONG NormalizeX(int x) { return LONG(x * 65535.0f / GetSystemMetrics(SM_CXSCREEN)); } static LONG NormalizeY(int y) { return LONG(y * 65535.0f / GetSystemMetrics(SM_CYSCREEN)); } /** * @brief 移动鼠标到指定屏幕坐标 */ static void MouseMove(int x, int y) { INPUT input{}; input.type = INPUT_MOUSE; input.mi.dx = NormalizeX(x); input.mi.dy = NormalizeY(y); input.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE; SendInput(1, &input, sizeof(INPUT)); } /** * @brief 鼠标左键点击 */ static void MouseLeftClick() { INPUT inputs[2]{}; inputs[0].type = INPUT_MOUSE; inputs[0].mi.dwFlags = MOUSEEVENTF_LEFTDOWN; inputs[1].type = INPUT_MOUSE; inputs[1].mi.dwFlags = MOUSEEVENTF_LEFTUP; SendInput(2, inputs, sizeof(INPUT)); } /** * @brief 鼠标右键点击 */ static void MouseRightClick() { INPUT inputs[2]{}; inputs[0].type = INPUT_MOUSE; inputs[0].mi.dwFlags = MOUSEEVENTF_RIGHTDOWN; inputs[1].type = INPUT_MOUSE; inputs[1].mi.dwFlags = MOUSEEVENTF_RIGHTUP; SendInput(2, inputs, sizeof(INPUT)); } }; #endif // INPUT_SIMULATOR_H /************************************************************ * 文件:main.cpp * 描述:模拟输入示例 ************************************************************/ #include <iostream> #include "InputSimulator.h" int main() { // 给用户 3 秒准备时间 Sleep(3000); // 模拟输入文字 InputSimulator::SendText("Hello World"); // 回车 InputSimulator::KeyPress(VK_RETURN); // 移动鼠标并点击 InputSimulator::MouseMove(500, 300); Sleep(500); InputSimulator::MouseLeftClick(); return 0; } 

六、代码详细解读(仅解读方法作用)


1. KeyDown / KeyUp

作用:

  • 构造键盘 INPUT
  • 提交到系统输入队列
  • 等价于真实按键

2. SendText

作用:

  • 将字符映射为虚拟键码
  • 自动处理 Shift
  • 逐字符发送输入

3. MouseMove

作用:

  • 使用绝对坐标模式
  • 精确移动到屏幕位置

4. MouseLeftClick / MouseRightClick

作用:

  • 组合 Down + Up
  • 模拟完整点击行为

七、项目详细总结

本项目完整实现了一个:

✅ 官方 API
✅ 行为稳定
✅ 工程可复用
✅ 教学价值高

C++ 键盘鼠标模拟模块

通过它,你应当真正理解:

  • Windows 输入事件是如何产生的
  • SendInput 的正确使用方式
  • 输入模拟与自动化测试的核心原理

八、项目常见问题及解答


Q1:为什么有些程序收不到模拟输入?

答:

  • 高权限程序需要同等权限
  • 某些游戏/安全软件会主动屏蔽
  • 这是系统安全策略,不是代码问题

Q2:可以模拟中文输入吗?

答:

  • 简单示例不支持
  • 中文需:
    • 输入法状态
    • 或使用 KEYEVENTF_UNICODE

Q3:会被系统识别为“假输入”吗?

答:

  • SendInput 是官方 API
  • 行为等同真实输入(但受安全策略限制)

九、扩展方向与性能优化


1. Unicode 输入支持

  • 使用 KEYEVENTF_UNICODE
  • 支持中文/多语言

2. 组合键支持

  • Ctrl + C / Alt + Tab
  • 自动化测试高频需求

3. 鼠标拖拽与滚轮

  • 拖动文件
  • 模拟滚动

4. UI 自动化封装

  • 结合窗口查找(FindWindow)
  • 构建自动化测试框架

Read more

JAVA API (三):从基础爬虫构建到带条件数据提取 —— 详解 URL、正则与爬取策略

JAVA API (三):从基础爬虫构建到带条件数据提取 —— 详解 URL、正则与爬取策略

个人主页-爱因斯晨 文章专栏-Java学习 相关文章:API (一) 相关文章:API(二) 持续努力中,感谢支持 一、爬虫基础 (一)爬虫的基本概念 * 定义:爬虫是按照一定规则自动抓取网络信息的程序,在 Java 环境下,可借助URL、HttpURLConnection等 API 来实现。 * 应用场景:广泛应用于数据采集,如电商平台的价格监控、各类新闻的聚合;还可用于信息分析,如舆情监测等。 (二)Java 实现简单爬虫的步骤 建立网络连接:利用URL类确定目标网页的地址,再通过openConnection()方法获取HttpURLConnection对象。 URL url =newURL("https://example.com");HttpURLConnection conn =(HttpURLConnection) url.openConnection(); 设置请求参数:

By Ne0inhk
基于Milvus与混合检索的云厂商文档智能问答系统:Java SpringBoot全栈实现

基于Milvus与混合检索的云厂商文档智能问答系统:Java SpringBoot全栈实现

基于Milvus与混合检索的云厂商文档智能问答系统:Java SpringBoot全栈实现 面对阿里云、腾讯云等厂商海量的产品文档、规格参数与价格清单,如何构建一个精准、高效的智能问答系统?本文将为你揭秘从技术选型到生产部署的完整方案。 云服务商的产品生态系统日益庞大,相关的技术文档、规格参数、定价清单等文档数量急剧增长。传统的文档查找方式已无法满足开发者和运维人员快速获取准确信息的需求。 基于检索增强生成(RAG)的智能问答系统成为解决这一难题的有效方案。本文将详细介绍如何使用 Java SpringBoot 和 Milvus 向量数据库,构建一个面向云厂商文档的高效混合检索问答系统。 一、核心挑战与架构选型 云厂商文档具有鲜明的技术特点,这些特点直接影响了我们的技术选择: 1. 高度结构化:包含大量技术规格表、价格矩阵和配置参数 2. 专业术语密集:如“ECS.g6.2xlarge”、“对象存储每秒请求数”等精确术语 3. 多格式混合:Markdown、PDF、Word、TXT等格式并存 4. 版本频繁更新:产品迭代快,文档需要及时同步

By Ne0inhk
【Java 开发日记】我们来说一下无锁队列 Disruptor 的原理

【Java 开发日记】我们来说一下无锁队列 Disruptor 的原理

目录 一、为什么需要 Disruptor?—— 背景与问题 二、核心设计思想 三、核心组件与原理 1. 环形缓冲区(Ring Buffer) 2. 序列(Sequence) 3. 序列屏障(Sequence Barrier) 4. 等待策略(Wait Strategy) 5. 事件处理器(EventProcessor) 6. 生产者(Producer) 四、工作流程示例(单生产者 -> 单消费者) 五、多消费者与依赖关系 六、总结:Disruptor 高性能的秘诀 一、为什么需要 Disruptor?—— 背景与问题 在高并发编程中,传统的队列(如 java.

By Ne0inhk
Java 大视界 -- Java 大数据在新能源微电网能量优化调度与虚拟电厂协同控制中的应用实践(282)

Java 大视界 -- Java 大数据在新能源微电网能量优化调度与虚拟电厂协同控制中的应用实践(282)

💖亲爱的朋友们,热烈欢迎来到 青云交的博客!能与诸位在此相逢,我倍感荣幸。在这飞速更迭的时代,我们都渴望一方心灵净土,而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识,也期待你毫无保留地分享独特见解,愿我们于此携手成长,共赴新程!💖 本博客的精华专栏: 【大数据新视界】 【Java 大视界】 【智创 AI 新视界】 【Java+Python 双剑合璧:AI 大数据实战通关秘籍】 社区:【青云交技术变现副业福利商务圈】和【架构师社区】的精华频道: 【福利社群】 【今日看点】 【今日精品佳作】 【每日成长记录】 Java 大视界 -- Java 大数据在新能源微电网能量优化调度与虚拟电厂协同控制中的应用实践(282) * 引言: * 正文: * 一、新能源微电网技术架构与 Java 基座 * 1.1 微电网控制的

By Ne0inhk