DInput and global key hooks (Vista/Win7)

Started by
14 comments, last by mattd 14 years, 5 months ago
I have an application I've written that needs to block certain key input. I use a WH_KEYBOARD_LL global key hook to do this. Recently I discovered that it no longer blocks key input for DInput in Vista or Win7. I'm interested if anyone knows what has changed in DInput for the global keyhook not to block key input anymore. It is quite often suggested on these forums (especially by Evil Steve) to use Windows messages for keyboard input instead. While I have always agreed with him it does raise an interesting question about DInput for Vista/Win7 in that something about it's method of reading input has changed. To quote Evil Steve; "(DInput) creates a seperate thread to just read data from the keyboard using raw input". But when my app is running on Vista/Win 7 it *can* block standard Raw Input key readings but *not* DInput key reads. On XP it blocks both. So it seems something about the way it's reading keyboard has changed. BTW This doesn't appear to be a UAC issue as I've tried running the app as Administrator as well as turning off UAC.
Advertisement
Quote:Original post by Headkaze
To quote Evil Steve; "(DInput) creates a seperate thread to just read data from the keyboard using raw input". But when my app is running on Vista/Win 7 it *can* block standard Raw Input key readings but *not* DInput key reads. On XP it blocks both. So it seems something about the way it's reading keyboard has changed.
Are you sure it's blocking input from WM_INPUT? From MSDN (emphasis added):
Quote:DirectInput is a set of API calls that abstracts input devices on the system. Internally, DirectInput creates a second thread to read WM_INPUT data, and using the DirectInput APIs will add more overhead than simply reading WM_INPUT directly.
I have created a set of binaries to prove that there is some sort of bug in Vista / Win7 relating to WH_KEYBOARD_LL global key hook. The bug seems to only effect DInput7 and not DInput8. I have not tested other versions of DInput.

You can download my test applications @ DInputBlockTest.zip

Inside Vista/Windows 7:

1. Launch DInput7.exe and DInput8.exe
2. Press the SPACE bar and you will see the apps output that you pressed SPACE to the console
3. Now launch KeyHook.exe
4. Press the SPACE bar and you will see the space key is now blocked in both DInput7.exe and DInput8.exe

Now close everything by pressing ESC

1. This time launch KeyHook.exe first
2. Now launch DInput7.exe and DInput8.exe
3. Press the SPACE bar and notice that only DInput8.exe's keys are being blocked

Here is a screenshot showing how DInput7.exe does not have it's keys blocked.

DInput7 Key Block Fail

You can run these apps in either order on XP and the key hook will always block the input. So I guess the question is, how can I have my global key hook running before a DInput7 app launches and still block it's input?
I don't see why a WH_KEYBOARD_LL hook should intercept WM_INPUT messages. WM_INPUT messages are not keyboard messages, they're raw input messages. They happen at a lower level than WM_KEYDOWN and WM_KEYUP.

If you want to intercept WM_INPUT messages, I think you'll have to use a WH_GETMESSAGE hook.
In the archive I posted is a console app called RawInput.exe that shows that WH_KEYBOARD_LL does indeed block Raw Input / WM_INPUT messages.

The mystery here is why it doesn't block DInput7 keyboard input in Win7/Vista while it does for DInput8.

The WH_GETMESSAGE does look interesting though..
It's not a bug, just normal hook behaviour. DInput7 doesn't use Raw input to get device messages, probably because it predates raw input support, rather it uses hooks for keyboard messages.

In your second test, the DInput7 hook is added after the one in keyhook.exe so it gets called before yours, passes it on and then you block it.

Edit:
Hmm, that would explain it but not the XP behaviour. A cursory check shows that the XP and 7 version of dinput.dll do things the same way. No idea then, if nobody can explain it I might investigate later if nothing else comes up.
I noticed your edit, and you're right it doesn't explain the reason why it works differently in XP.

If you look at dinput.dll in a hex editor you can see the API function "GetRawInputData" referenced in it. So the mystery continues?
Quote:Original post by Headkaze
The mystery here is why it doesn't block DInput7 keyboard input in Win7/Vista while it does for DInput8.


It's entirely possible that this was a bug in XP and was fixed for Vista, or just that the behavior was changed. Traditionally Microsoft tends not to do this since it can break existing programs that rely on the old behavior, but lately they've been more aggressive on this front (especially if it's something that affects security).

I had a look and it is a bug in DInput.dll's version checking code. On OS's with a major version higher than 5, the flag to use Raw Input processing isn't set. When that flag isn't set, low-level hooks are installed to monitor both mouse and keyboard input. XP and it's major version of 5, sets the raw input flag so it uses that to get device data instead of the hooks, which are never installed.
Thanks adeyblue that is really helpful. If you have any ideas on a workaround for this I'm all ears. Incidently how did you discover that info?

According to MSDN:

Quote:The SetWindowsHookEx function always installs a hook procedure at the beginning of a hook chain.


So dinput.dll is using a hook instead of Raw Input because of a version issue and this hook is created at the top of the hook chain and thus prevents my app from blocking keys. So the question now is; is it possible to always stay at the beginning of a hook chain?

I found an interesting compatibility app called the IgnoreAltTab Fix

Quote:This fix works by calling the SetWindowHookEx(WH_KEYBOARD_LL, ...) function to install a low-level keyboard hook, which then implements a LowLevelKeyboardProc function to handle key presses and filter out the following key combinations:


Now the interesting part:

Quote:This fix attempts to stay at the end of the hook chain by hooking as early as possible and by allowing previous keyboard hooks to attempt to process the keyboard events before the fix processes them out.


Unfortunately it's doing the opposite of what I want, and that is to stay at the top of the hook chain. This program attempts to stay at the end of the hook chain which would naturally occur if you install the hook "as early as possible".

Quote:The IgnoreAltTab compatibility fix intercepts the RegisterRawInputDevices API, which causes the call to fail with an ERROR_INVALID_PARAMETER code and prevents the delivery of the WM_INPUT messages. This delivery failure forces the included hooks to be ignored and forces DInput to use Windows-specific hooks.


This is also interesting, and proves that DInput will fall back on using a keyboard hook if RegisterRawInputDevices fails. I'm not sure why they would do this for this particular app though, because in my experience Raw Input reading is easier to block using WH_KEYBOARD_LL than a global hook is. If they are forcing DInput to use hooks then how are they staying at the beginning of the hook chain to block the key presses?

[Edited by - Headkaze on October 15, 2009 7:41:33 AM]

This topic is closed to new replies.

Advertisement