将单元素数组传递给 SendInput 是错误吗?

Is it a bug to pass a single-element array to SendInput?

给定以下代码

void foo() {
    INPUT input{};
    input.type = INPUT_MOUSE;
    input.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
    SendInput(1, &input, sizeof(input));
    input.mi.dwFlags = MOUSEEVENTF_LEFTUP;
    SendInput(1, &input, sizeof(input));
};

在连续调用中将单元素数组传递给 SendInput 是错误吗?这似乎得到了文档的完美支持。

简答:也许。

更长的答案:视情况而定。

查看它依赖于什么,以及当它重要时,它有助于理解为什么 SendInput was introduced into the Windows API: For one, it consolidates the keybd_event and mouse_event API 进入单个 API 调用。更重要的是,它增加了一个重要的特性,这是以前调用所没有的。这在文档中被调用:

The SendInput function inserts the events in the INPUT structures serially into the keyboard or mouse input stream. These events are not interspersed with other keyboard or mouse input events inserted either by the user (with the keyboard or mouse) or by calls to keybd_event, mouse_event, or other calls to SendInput.

换句话说:SendInput 建立注入输入序列的原子性,而不考虑调用代码控制之外的外部事件。

当输入由一系列单独的事件组成时(如问题中所示),自动注入输入通常很重要。该代码在对 SendInput 的 2 个单独调用中注入一个按下鼠标按钮,然后注入一个鼠标按钮。虽然目的是有一个鼠标单击事件,但实现允许其他输入源散布输入。当另一个输入源在鼠标按钮按下和按下事件之间产生鼠标移动事件时,预期的单击已变成拖放操作。完全相同的代码没有在文件资源管理器中选择文件,而是将文件扔进了回收站。这显然构成了一个错误。

同样,注入由组合键组成的键盘输入通常需要原子性保证。注入 Ctrl+C 要求所有四个输入事件都在一个事务中。否则,(恶意的)输入源可能会在 Ctrl 键按下后立即合成一个 Ctrl 键按下事件,留下代码注入 C,带有一个杂散的 Ctrl 键事件尾随。这可能也不是我们想要的。

总结:重复调用 SendInput 是一个错误,如果满足以下条件,则将 1 作为第一个参数传递:

  • 输入由一系列单独的输入事件组成。
  • 输入需要被解释为一个单元。