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 开发日记】我们来说一下无锁队列 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

2026终极版|Spring Boot 3.5.11 + JDK21 整合 RabbitMQ / RocketMQ / Kafka(对比 + 选型 + 可运行示例)

适配环境:JDK 21(LTS)、Spring Boot 3.5.11 适用人群:Java 后端开发、架构师、技术选型决策者 特点:基于 Spring Boot 3.5.x + JDK21 实战验证,代码可直接运行,避免常见版本与虚拟线程误用问题 一、技术背景 1️⃣ JDK21 JDK21 是当前长期支持版本(LTS),虚拟线程(Project Loom)正式 GA,大幅降低高并发场景下的线程资源占用成本。 2️⃣ Spring Boot 3.5.11 Spring Boot 3.5.11 为

By Ne0inhk
深度解析网络编程套接字:从 Socket 底层原理到 Java 高性能实战

深度解析网络编程套接字:从 Socket 底层原理到 Java 高性能实战

【深度长文】攻克网络编程套接字:从底层协议原理到 Java 高性能实战 我的主页:寻星探路个人专栏:《JAVA(SE)----如此简单!!! 》《从青铜到王者,就差这讲数据结构!!!》 《数据库那些事!!!》《JavaEE 初阶启程记:跟我走不踩坑》 《JavaEE 进阶:从架构到落地实战 》《测试开发漫谈》 《测开视角・力扣算法通关》《从 0 到 1 刷力扣:算法 + 代码双提升》 没有人天生就会编程,但我生来倔强!!! 寻星探路的个人简介: 一、 引言:网络编程的时代意义 在数字化浪潮中,我们不仅是信息的消费者,更是信息的传输者。从简单的网页浏览到支撑亿级并发的分布式系统,其底层基石都是网络编程。网络编程的本质,是跨越物理空间的限制,实现不同计算机上进程间的通信。 网络编程打破了单机系统的局限,使得我们可以利用全球范围内的计算资源。本文将基于 Socket 套接字的核心技术,深入剖析传输层两大核心协议 TCP

By Ne0inhk