When you synthesize input with SendInput, you are also synthesizing the timestamp


A customer was reporting a problem when they used the Send­Input function to simulate a drag/drop operation for automated testing purposes.

I see the mouse move from one location to another, and the starting and stopping locations are correct on the screen, but the mouse moves instantaneously rather than waiting 500ms between operations. Here's how I'm sending the input.

INPUT input[3] = { 0 };

// Click
input[0].type = INPUT_MOUSE;
input[0].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
input[0].mi.time = 500;

// Drag
input[1].type = INPUT_MOUSE;
input[1].mi.dwFlags = MOUSEEVENTF_MOVE;
input[1].mi.dx = 100;
input[1].mi.dy = 100;
input[1].mi.time = 1000;

// Release
input[2].type = INPUT_MOUSE;
input[2].mi.dwFlags = MOUSEEVENTF_LEFTUP;
input[2].mi.time = 500;

SendInput(3, input, sizeof(INPUT));

Well, yeah, all the events occur immediately because you submitted them all at once.

The time field in the MOUSE­INPUT structure is not for introducing delays in playback. Though I'm not sure what the customer thought the time field was. They say that they want a 500ms delay between operations. At first, I thought that they may have misinterpreted it as a delay relative to the time the Send­Input call is made, since they set input[0].mi.time to 500 and input[1].mi.time to 1000. But if thay were the case, then setting input[2].mi.time to 500 would end up going backward in time. But looking at the big picture, it's probably not worth trying to figure out what they were thinking, since that code will have to be scrapped anyway.

The time field is for letting an input source (typically a hardware device) say, "Hi, um, the mouse left button went down at 9:30 this morning. Yes, I know it's already 10am. The PCI bus got a flat tire, and then the spare was also flat, and really there's no point going into the details. Sorry this message arrived late." The window manager (and anybody else who bothers to check the time member of the MSG structure) uses this information to do things like detect double-clicks. If the input source later reports, "Hi, um, the mouse left button went up at 9:30:00.100 this morning, sorry for the late report," the window manager says, "Well, that was only 100 milliseconds after the button went down thirty minutes ago, so I guess that's a double-click after all. Could you try to be a bit more prompt with this information in the future?" (Sarcasm added.)

In other words, the time member of the MOUSE­INPUT structure is for backdating input events. They still get delivered immediately, but the timestamp allows the window manager (and other code which looks at the timestamp) to make decisions about how they should respond.

Note that post-dating the timestamp does not cause the input delivery to be delayed, and back-dating the timestamp does not cause the input to be inserted into the input stream ahead of other input. The input is merely delivered with a timestamp in the future or in the past. (And who knows what sort of havoc that will create if a program checks the timestamps and notices that they are either from the future or have traveled back in time. Maybe you'll get a call from Microsoft Research asking for more information about your time machine.)

If you want three input events to take place with a 500ms delay between them, then you need to call Send­Input three times, with a 500ms delay between the calls.

Comments (6)
  1. Anonymous says:

    "Maybe you'll get a call from Microsoft Research asking for more information about your time machine."

    AHA!  Proof that Microsoft will have stolen everything!  Raymond said it so it must be true.

  2. Anonymous says:

    I have a large vertical rotating cylinder that can be used as a time machine, but not that effectively.

  3. Anonymous says:

    Aha, so that must be how the window manager correctly detects double clicks even when the computer is lagging so badly that it takes seconds before the action actually happens?  I always assumed the mouse driver was running at real time priority and preempted everything else.

  4. MItaly says:

    @Brian: "You should almost never use REALTIME_PRIORITY_CLASS, because this interrupts system threads that manage mouse input, keyboard input, and background disk flushing." (msdn.microsoft.com/…/ms685100%28VS.85%29.aspx), so the mouse driver runs at a lower priority. (also, IIRC Raymond talked about it once)

  5. Anonymous says:

    The PCI bus got a flat tire

    That had me in stitches. That's gold, Jerry! Gold!

  6. Anonymous says:

    While I understand why the SendInput superseded keybd_event and mouse_event I still like those for simulating mouse / keyboard events. Heck even the counterpart mi / ki of INPUT structure were taken from there. And while SendInput is more useful for doing tricky events most of the times you only need "click here, click there, press ALT+TAB, etc etc" which is easier to code with keybd_event / mouse_event.

Comments are closed.