views:

187

answers:

2

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?

+1  A: 

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.

Brian Jorgensen
@Brian. Thanks. I'm developing on Windows 7. If I intercept the KeyDown with a low-level Keyboard Hook then my code works fine (without having to simulate a KeyUp). However, this means that the windows key is completely disabled, and ideally I'd like the Windows key's functionality to be unaffected. Also, the documentation states you should "correct as necessary" immediately before the call to SendInput. Do you think this is actually impossible to do?
Rich
You can intercept the Windows key only in the specific cases you need, and let it pass through otherwise. Then you retain the normal functionality. To let it pass through, end your handler proc with CallNextHookEx (http://msdn.microsoft.com/en-us/library/ms644974(v=VS.85).aspx)
Brian Jorgensen
@Brian, but when the Windows key is pressed, I don't know whether or not it's going to be one of the specific cases I need. I only find that out when I decide to call `SendInput`, which is after the Windows key has already been held down.
Rich
You have to track the state of the keys you care about through their KeyDown/KeyUp cycle. If you read my original answer, I was doing exactly this with the combination WinKey+L. WinKey still works with every other combination, and functions as expected is you press and release it without a combination (opens the Start menu). The only downside is that "WinKey down" messages are delayed, but then sent only after "WinKey up" is intercepted. Fortunately, "WinKey down" is not a very important event for most purposes.
Brian Jorgensen
But your requirements are different to mine. You're swapping the Win and Ctrl Keys. What happens if you hold down the actual Windows key and press D? In that instance you are capturing the Windows keypress and simulating a Ctrl+D event instead. I need *some* win key presses to act as normal, but *other* ones to be remapped. You are remapping *all* of them to Ctrl presses. (And vice versa) Or is there something about your original answer I'm missing?
Rich
Thanks for your help so far, btw. It's appreciated!
Rich
A: 

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

Brian Jorgensen
I don't see how remapping keyboard scan code would help me. I want the Win key not to affect input I simulate with SendInput, but I still want it to work normally otherwise.
Rich