最可靠方法是windows用sendinput逐字符发送并设keyeventf_unicode标志,linux用uinput需root权限,macos用cgeventkeyboardsetunicodestring;均需处理焦点和输入法状态。

Windows平台用SendInput逐字符发送最可靠
直接调用SendInput是模拟键盘输入字符串的主流做法,它绕过窗口焦点限制,比keybd_event更符合现代Windows输入模型。关键不是“发一串”,而是把字符串拆成单个wchar_t,对每个字符生成对应的虚拟键码和扫描码。
常见错误是直接用MapVirtualKey处理ASCII字符——这在中文、符号或Shift修饰场景下会失效。正确做法是用ToUnicodeEx反查键码,或更稳妥地:对可打印字符走VK_PACKET路径(仅限Unicode输入)。
- 英文和数字:用
MapVirtualKey+SendInput组合,需注意Caps Lock状态 - 中文/Emoji/特殊符号:必须用
INPUT_KEYBOARD类型 +KEYEVENTF_UNICODE标志,wScan字段填UTF-16码元 - 避免硬编码
VK_A等键码——不同键盘布局下映射可能变化
SendInput发送中文时必须设KEYEVENTF_UNICODE
不设这个标志,wScan会被当扫描码忽略,导致中文全变成乱码或无响应。Windows只认wScan为Unicode码点(非UTF-8),且要求输入法已处于激活状态(否则字符进不到编辑框)。
实操中容易漏掉两件事:一是没在INPUT结构体里清零未用字段(dwExtraInfo等),二是没配对发送按下/弹起事件。一个汉字至少触发两次SendInput调用。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 示例:发送
L"你好",需循环处理每个wchar_t:input.ki.wScan = L'你'; input.ki.dwFlags = KEYEVENTF_UNICODE; - 每次发送后加
Sleep(20)——太快会导致输入法来不及消化 - 目标窗口必须有输入焦点,否则
SendInput仍生效但内容进不到控件内
Linux下用uinput设备节点模拟需root权限
没有类似SendInput的系统级API,必须通过/dev/uinput创建虚拟键盘设备。普通用户默认无权限,得加udev规则或临时用sudo,这点和Windows本质不同。
核心步骤是:打开设备 → 写入支持的事件类型(EV_KEY)→ 设置允许的键码(KEY_A到KEY_Z等)→ 用write()发struct input_event序列。字符串要转成对应键码,比如'a'对应KEY_A,大写则需先发KEY_LEFTSHIFT按下。
- 中文输入依赖前端输入法(如fcitx5),
uinput只管按键,不负责上屏逻辑 -
libevdev或python-uinput封装了部分细节,但底层仍是ioctl和write - 别忘了最后调用
ioctl(fd, UI_DEV_DESTROY)释放设备,否则下次创建失败
跨平台方案别碰Robot类库的“字符串输入”接口
像Qt的QTest::keyClicks或Java的Robot.keyPress批量接口,表面看能直接传字符串,实际内部仍是逐字符分解+键码映射。问题在于它们默认按当前系统键盘布局查表,遇到非美式键盘(如德语QWERTZ)或自定义布局时,'@'可能按出'ä'。
更麻烦的是,这类封装通常不暴露KEYEVENTF_UNICODE或uinput的底层控制权,调试时连日志都看不到真实发出的键码。真要跨平台,不如自己写一层薄封装:Windows走SendInput,Linux走uinput,macOS走CGEventPost,统一输入UTF-16字符串。
- macOS上
CGEventKeyboardSetUnicodeString能直接塞字符串,但仅支持前台应用,且SIP可能拦截 - 所有方案都绕不开焦点问题——模拟输入≠自动聚焦,得额外调用
SetForegroundWindow或xdo_set_active_window - 安全软件常拦截
SendInput和uinput,测试时关掉实时防护再试
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










