You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Windows C++ API向窗口发送指定键盘来源的输入?

嘿,我来帮你搞定这个问题!你现在用SendInput模拟键盘输入,但没法指定输入来自哪台特定键盘对吧?这就给你一步步拆解怎么做~

如何用Windows C++ API发送来自特定键盘的输入

首先得明确:你原来的代码确实能实现输入模拟,但系统默认会把这类输入标记为「通用注入输入」,不会关联到任何物理键盘。要让输入被识别为来自某台特定键盘,需要分两步操作:枚举目标键盘设备,再结合设备信息构造专属的输入结构体。

步骤1:先找到目标键盘的设备信息

首先你得把系统里所有的键盘设备找出来,筛选出你要指定的那台。可以用Windows的Raw Input API来枚举设备,代码示例如下:

#include <Windows.h>
#include <vector>
#include <iostream>

// 枚举所有连接的键盘设备,返回设备句柄列表
std::vector<HANDLE> EnumKeyboardDevices() {
    std::vector<HANDLE> keyboardHandles;
    UINT numDevices = 0;
    
    // 先获取系统中输入设备的总数
    if (GetRawInputDeviceList(nullptr, &numDevices, sizeof(RAWINPUTDEVICELIST)) == 0) {
        std::vector<RAWINPUTDEVICELIST> deviceList(numDevices);
        if (GetRawInputDeviceList(deviceList.data(), &numDevices, sizeof(RAWINPUTDEVICELIST)) != (UINT)-1) {
            for (const auto& device : deviceList) {
                // 只筛选键盘类型的设备(RIM_TYPEKEYBOARD)
                if (device.dwType == RIM_TYPEKEYBOARD) {
                    char deviceName[256];
                    UINT nameSize = sizeof(deviceName);
                    // 获取设备的唯一标识名称(比如USB路径),用来区分不同键盘
                    if (GetRawInputDeviceInfoA(device.hDevice, RIDI_DEVICENAME, deviceName, &nameSize) != (UINT)-1) {
                        std::cout << "找到键盘设备: " << deviceName << std::endl;
                        keyboardHandles.push_back(device.hDevice);
                    }
                }
            }
        }
    }
    return keyboardHandles;
}

运行这段代码后,会输出所有连接的键盘设备名称,你可以根据这个名称找到你要指定的那台键盘对应的句柄。

步骤2:构造关联特定键盘的输入

拿到目标键盘的句柄后,就可以修改SendInput的输入结构体,让输入和这台键盘绑定:

#include <Windows.h>

int main() {
    // 先枚举键盘,获取目标键盘的句柄(这里取第一个键盘,你可以根据实际设备名称选择)
    auto keyboardHandles = EnumKeyboardDevices();
    if (keyboardHandles.empty()) {
        return 1;
    }
    HANDLE targetKeyboard = keyboardHandles[0];

    // 获取目标键盘对应的扫描码(以VK_RIGHT为例)
    // 用MapVirtualKeyEx可以针对特定键盘布局/设备转换键码
    UINT scanCode = MapVirtualKeyEx(VK_RIGHT, MAPVK_VK_TO_VSC, GetKeyboardLayout(0));
    
    // 处理扩展键的情况(比如方向键、功能键这类)
    DWORD flags = KEYEVENTF_SCANCODE;
    if ((GetKeyState(VK_MENU) & 0x8000) || (scanCode & 0xE000)) {
        flags |= KEYEVENTF_EXTENDEDKEY;
    }

    // 构造键盘输入结构体
    KEYBDINPUT ki = {
        0,  // 用扫描码的话,wVk字段设为0即可
        scanCode,
        flags,
        0,
        (ULONG_PTR)targetKeyboard  // 把目标键盘的句柄放进dwExtraInfo,绑定输入来源
    };

    INPUT input = {
        INPUT_KEYBOARD,
        {.ki = ki}
    };

    // 发送按键按下事件
    SendInput(1, &input, sizeof(INPUT));

    // 构造并发送按键释放事件(模拟完整的按键操作)
    ki.dwFlags |= KEYEVENTF_KEYUP;
    input.ki = ki;
    SendInput(1, &input, sizeof(INPUT));

    return 0;
}

关键细节说明

  • KEYEVENTF_SCANCODE标志:使用扫描码而非虚拟键码,能更精准地匹配特定键盘的硬件输入逻辑,避免不同键盘布局的干扰。
  • dwExtraInfo字段:这里传入目标键盘的句柄,系统会把这个信息附加到输入事件中,让系统或监听输入的程序能识别出这个输入来自哪台键盘。
  • 设备区分:如果有多台键盘,一定要通过GetRawInputDeviceInfo返回的设备名称来筛选,每个物理键盘的名称都是唯一的(比如包含USB端口信息)。

小提醒

不是所有应用都会检查输入的来源设备——很多程序只关心按键的键值,不会验证输入是不是来自特定键盘。但如果你的需求是让输入被系统或特定程序识别为某台物理键盘的输入,上面的方法完全可行。

内容的提问来源于stack exchange,提问作者Nathan Wood

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 15:57:53