SendInput() 函数¶
模拟键盘的一种常见方法是 SendInput() Windows API 函数。这个函数向最顶层窗口发送键盘和鼠标输入,原型如下:
第一个参数 inputCount 是要发送的输入数量。对于本书的示例,我总是使用值 1。第二个参数 inputs 是指向预定义类型 INPUT 的结构(或长度与 inputCount 值匹配的结构数组)的指针。最后一个参数是 inputs 在内存中的大小,用公式 size =inputCount × sizeof(INPUT) 计算。
INPUT 结构告诉 SendInput() 函数要发送什么类型的输入,以下代码展示了如何初始化一个 INPUT 实例来按下 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() 四次,但这仍然比使用没有包装函数的代码容易。