关于winapi:将单元素数组传递给SendInput是一个错误吗?

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

给出以下代码

1
2
3
4
5
6
7
8
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是一个错误吗?这似乎得到了文档的完美支持。


简短答案:也许。

更长的答案:取决于。

要查看它所依赖的内容,以及何时重要,它有助于理解Windows API中为何引入了SendInput:例如,它将keybd_event和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作为第一个参数传递是一个错误:

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