一、项目背景
在实际工程中,我们经常让程序像'人'一样操作电脑:
- 自动化测试:点击按钮、输入文本
- 运维/工具:批量操作 GUI 程序
- 辅助工具:快捷操作、脚本化流程
- 教学实验:理解 OS 输入事件链路
这些需求的核心,都是:
程序如何向操作系统'注入'键盘与鼠标输入事件?
1. Windows 输入系统的基本认知
Windows 中,键盘和鼠标并不是直接'给应用'的,而是:
硬件 → 驱动 → 系统输入队列 → 消息分发 → 窗口
只要我们向系统输入队列提交合法事件,系统就会像处理真实硬件一样处理它。
2. 常见实现方式对比
| 方式 | 特点 | 是否推荐 |
|---|---|---|
| keybd_event / mouse_event | 老 API | ❌ 已过时 |
| SendInput | 官方推荐 | ✅ |
| 驱动层 | 复杂/高风险 | ❌ |
| 注入 DLL | 非必要 | ❌ |
本文只使用官方推荐的 SendInput。
3. 本项目目标
实现一个教学 + 工程可用的输入模拟模块,支持:
- 键盘按下 / 抬起
- 鼠标移动
- 鼠标点击(左 / 右)
- 简单组合输入
- 清晰、可复用的封装
二、项目需求
1. 功能需求
实现以下能力:
- 模拟单个键按下与释放
- 模拟字符串输入
- 模拟鼠标移动到屏幕坐标
- 模拟鼠标左键 / 右键点击
- 支持组合示例(键盘 + 鼠标)
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 / KeyUpSendTextMouseMoveMouseClick
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)
- 构建自动化测试框架

