Rich
Rich

Reputation: 7796

How can I deal with depressed Windows logo key when using `SendInput`?

My application synthesises keystrokes in other apps by using SendInput. This almost works, except that due to circumstances I cannot change, the left Windows key will always be physically depressed when sending the input. This means that if, for example, the keystroke being sent is the 'd' key, then Windows sees the keystroke as its Win+D shortcut and displays the Desktop, and the 'd' keystroke never makes it to its intended target.

The documentation mentions this problem but is frustratingly vague about how to fix it (emphasis mine):

This function does not reset the keyboard's current state. Any keys that are already pressed when the function is called might interfere with the events that this function generates. To avoid this problem, check the keyboard's state with the GetAsyncKeyState function and correct as necessary.

How should I "correct as necessary"?

I've tried using SetKeyboardState to turn off the keyboard state of the VK_LWIN key, after first attaching my thread to the thread of the window that I am sending the keystrokes to (with AttachThreadInput), but this doesn't seem to affect the behaviour at all.

I've also tried inserting a KEYEVENTF_KEYUP event at the start of the input I am sending with SendInput, but this causes the Start Menu to pop up and grab focus, which is obviously not acceptable.

What is the correct method of dealing with a depressed Windows logo key when calling SendInput?

EDIT:

As per @Brian's answer I can prevent the win key from interfering with my code by installing a low-level keyhook which traps win-key keydown events.

This, however, has the undesired effect that the Windows key is now disabled entirely. I can sort of envisage some solution to this where I keep a note of the state of all the keys within my keyhook, and simulate presses of the windows key when other keys are pressed, depending on whether or not they are the ones I'm simulating, but that sounds like a hack to me. Alternatively, I could use my keyhook to emulate important windows keyboard shortcuts.

But the documentation implies it should all work in a much more straightforward way. Brian's solution is to prevent the win key from getting to the pressed down state in the first place. But the documentation states that you should be able to check if it's pressed down later on, and correct it if it is.

Is it actually possible to do this?

Upvotes: 3

Views: 3157

Answers (4)

Jeff Henning
Jeff Henning

Reputation: 1

I just had exactly the same problem and stumbled upon this post. In my case, the "hot key" that I was capturing included <Ctrl><Shift>, which were still being held down by the user (me for testing) when I used SendInput to send my keystrokes to the target application. Part of my SendInput sequence was a <DEL> key (VK_DELETE), which does not register a keystroke at all if modified by the <Ctrl> or <Shift> keys the user used to trigger the hot hey.

I found @Lexikos answer to be very helpful. I got around the side-effects by:

  1. Only responding to the keyboard message when my target window was the Foreground Window (using GetForgroundWindow() and checking the active window's title). Otherwise, just let the message pass to the next hook.
  2. Since I'm only responding to keystrokes to my target application (active window), consuming the keystrokes in the message queue and do not pass them on.
  3. Inserting both a key down event (no dwFlags set) followed by a key up event (KEYEVENTF_KEYUP) to release the virtual key.

I did find a few snags of my own:

  • I had to put KEYDOWN/KEYUP inputs for both the Left and Right Shift keys (VK_LSHIFT and VK_RSHIFT) to cover both cases. Using just VK_SHIFT did not work for me.
  • I had to put KEYDOWN/KEYUP inputs for both the Left and Right Ctrl keys (VK_LCONTROL and VK_RCONTROL) to cover both cases. Again, using just VK_CONTROL did not work for me.
  • The right <Ctrl> key is an extended key on the 101 Keyboard, so both the key down and key up inputs needed to have dwFlags set to KEYEVENTF_EXTENDEDKEY or it did not register and release the right <Ctrl> key.
  • The KEYDOWN/KEYUP message to clear the modifier keys must be sent in the same INPUT array, otherwise modifier key messages will slip back into the message queue and activate them again.

@Lexikos suggestion of sending an inert keystroke before these modifier resets may be valid; my sequence sends some UNICODE characters first that are not affected by the modifiers and then I reset the modifier keys right before susceptible virtual key. Here's a code snippet for resetting the <Ctrl><Shift> modifiers in my INPUT array:

// ^^^ Other keystrokes inserted above ^^^

// Issue KEYDOWN/KEYUP for <Control> & <Shift> as user is likely still holding them down. ********
// Have to release both left and right, in case user is using keys on either side of keyboard
keys[6].type = INPUT_KEYBOARD;
keys[6].ki.wVk = VK_RCONTROL;                      // Right <Ctrl>
keys[6].ki.dwFlags = KEYEVENTF_EXTENDEDKEY;        // Right <Ctrl> key is actually an extended key
                                                   // dwFlags must be set to KEYEVENTF_EXTENDEDKEY

keys[7] = keys[6];                                 // Right <Ctrl>
keys[7].ki.dwFlags |= KEYEVENTF_KEYUP;             // (UP)   OR the KEYUP flag with EXTENDEDKEY

keys[8].type = INPUT_KEYBOARD;
keys[8].ki.wVk = VK_LCONTROL;                      // Left <Ctrl>

keys[9] = keys[8];                                 // Left <Ctrl>
keys[9].ki.dwFlags = KEYEVENTF_KEYUP;              // (UP)   set the KEYUP flag

keys[10].type = INPUT_KEYBOARD;                    // 
keys[10].ki.wVk = VK_RSHIFT;                       // Right <Shift>

keys[11] = keys[10];                               // Right <Shift>
keys[11].ki.dwFlags = KEYEVENTF_KEYUP;             // (UP)   set the KEYUP flag

keys[12].type = INPUT_KEYBOARD;                    // 
keys[12].ki.wVk = VK_LSHIFT;                       // Left <Shift>

keys[13] = keys[12];                               // Left <Shift>
keys[13].ki.dwFlags = KEYEVENTF_KEYUP;             // (UP)   set the KEYUP flag

// vvv Send unmodified virtual key next vvv

Of course, a Down/Up pair could easily be inserted here for the <Win> key as well.
I hope this helps somebody else through this issue.

Upvotes: 0

Lexikos
Lexikos

Reputation: 1067

"Correcting" the state of a modifier key by inserting a KEYEVENTF_KEYUP event may have side-effects, but they can be avoided by inserting additional events.

The Start menu is triggered when the Win key is pressed and released, except when there is an intervening key event or if it was pressed in combination with another modifier. So instead of inserting just Win-up, insert Ctrl-down, Win-up, Ctrl-up.

Aside from the Start menu, releasing modifier keys might have other side-effects:

  • Releasing Alt can activate the active window's menu (even if it doesn't appear to have one).
  • Releasing Alt or Shift can activate the Alt+Shift language hotkey.
  • Releasing Ctrl or Shift can activate the Ctrl+Shift language hotkey.
  • Releasing any modifier key can activate the Office hotkey (in Windows 1903 and later, pressing and releasing Ctrl+Alt+Shift+Win opens the Office app).

Inserting a key-down and key-up (or possibly just a key-up without a prior key-down) with almost any other virtual keycode first will generally prevent any of these hotkeys from activating. One can use an undefined or reserved keycode such as 0xE8, although there is still a risk that another program makes use of it.

Upvotes: 2

Brian Jorgensen
Brian Jorgensen

Reputation: 1228

There is not a more "straightforward way" for a program to do this without modifying the registry. The ideal solution is to track the keypresses in a table (std::map, Dictionary, etc). The intent of this on MS's part is to prevent viruses/malware from taking control of the keyboard and preventing the user from using important keyboard combos such as WinKey-L and Ctrl-Alt-Delete.

You can, however, remap the keyboard scan codes in the registry if keyboard hooking is not for you. This isn't ideal, since it's hard to reverse (especially if you make a mistake, or your program crashes, or the user uninstalls the application but the registry hack is not reverted). But it does work. Here are some references:

http://www.howtogeek.com/howto/windows-vista/map-any-key-to-any-key-on-windows-xp-vista/

http://www.howtogeek.com/howto/windows-vista/disable-caps-lock-key-in-windows-vista/

http://www.usnetizen.com/fix_capslock.php

Upvotes: -1

Brian Jorgensen
Brian Jorgensen

Reputation: 1228

I basically solved this exact problem yesterday :) My code is in C#, but it mostly consists of win32 API calls via p/invoke, so it should translate directly to C++.

The solution is to use a Low-Level Keyboard Hook to intercept the initial Windows key KeyDown event (and tell the OS that you handled it so that it won't be passed to other Applications and/or the OS). Then, depending on OS, simulate the Windows key KeyUp event before your input. It sounds like you're half-way there, you just need to intercept the initial KeyDown.

My solution is targeted at Windows Vista and Windows 7, so if you're in XP or below, it's possible that you don't need to send the KeyUp. That's what I'll be testing soon, it may work in both; however, Vista and 7 intercept the windows key more aggressively than XP, hence my suspicions.

Upvotes: 2

Related Questions