跳转至

SendInput() 函数

模拟键盘的一种常见方法是 SendInput() Windows API 函数。这个函数向最顶层窗口发送键盘和鼠标输入,原型如下:

UINT SendInput(UINT inputCount, LPINPUT inputs,
int size);

第一个参数 inputCount 是要发送的输入数量。对于本书的示例,我总是使用值 1。第二个参数 inputs 是指向预定义类型 INPUT 的结构(或长度与 inputCount 值匹配的结构数组)的指针。最后一个参数是 inputs 在内存中的大小,用公式 size =inputCount × sizeof(INPUT) 计算。

INPUT 结构告诉 SendInput() 函数要发送什么类型的输入,以下代码展示了如何初始化一个 INPUT 实例来按下 F1 键:

INPUT input = {0};
input.type = INPUT_KEYBOARD;
input.ki.wVk = VK_F1;

要让机器人实际按下 F1,你需要发送这个输入两次,像这样:

SendInput(1, &input, sizeof(input));
//  input 改为按键释放
input.ki.dwFlags |= KEYEVENTF_KEYUP;
SendInput(1, &input, sizeof(input));

第一次调用 SendInput() 按下 F1,第二次释放它。释放不是因为输入发送了两次,而是因为第二次调用在 input 参数的键盘标志字段中启用了 KEYEVENTF_KEYUP 标志。由于即使单个键的设置也有点乱,最好把所有内容包装在函数中。结果看起来像清单 10-3。

void sendKeyWithSendInput(WORD key, bool up)
{
    INPUT input = {0};
    input.type = INPUT_KEYBOARD;
    input.ki.wVk = key;
    input.ki.dwFlags = 0;
        input.ki.dwFlags |= KEYEVENTF_KEYUP;
    SendInput(1, &input, sizeof(input));
}
sendKeyWithSendInput(VK_F1, false); // 按下
sendKeyWithSendInput(VK_F1, true); // 释放

清单 10-3:用 SendInput() 模拟按键的包装器

这个函数用给定的键初始化 input,如果设置了 up 就启用 KEYEVENTF_KEYUP 标志,并调用 SendInput() 函数。这意味着 sendKeyWithSendInput() 必须被二次调用来发送按键释放,即使释放总是必需的。函数这样编写是因为涉及 shift、alt 或 ctrl 等修饰键的键组合必须以不同方式发送;修饰键的按下必须在键的按下之前,但它的释放必须在键的释放之后。

以下代码展示了如何使用 sendKeyWithSendInput() 函数告诉机器人按 shift-F1:

sendKeyWithSendInput(VK_LSHIFT, false); // 按下 shift
sendKeyWithSendInput(VK_F1, false); // 按下 F1
sendKeyWithSendInput(VK_F1, true); // 释放 F1
sendKeyWithSendInput(VK_LSHIFT, true); // 释放 shift

你必须调用 sendKeyWithSendInput() 四次,但这仍然比使用没有包装函数的代码容易。