views:

580

answers:

4

I'm trying to write an application that responds whenever the Shift key is pressed, no matter what application currently has focus.

I tried this with SetWindowsHookEx() and with GetKeyboardState(), but both of these only work when the application's window has focus. I need it to work globally.

How do I do this?

+1  A: 

Setup a CBT hook look here http://www.codeproject.com/KB/DLL/keyboardhook.aspx

SDX2000
+3  A: 

You'll have to use SetWindowsHookEx(). There are only two types of hooks that you can implement in a managed language, WH_KEYBOARD_LL and WH_MOUSE_LL. All other hooks require a DLL that can be injected into another process. Managed DLLs cannot be injected, the CLR cannot be initialized.

This blog post has a functional example.

Hans Passant
Most other hooks can also be implemented in a managed language but only if they are restricted to threads of the current process. Low level mouse and keyboard hooks are the only 'global' hooks allowed.
Stephen Martin
This only grazes the surface of the problem and doesn't really go into any level of depth.
romkyns
This is a very specific answer to the specific question. It is a bit short and glosses over some information for simplicity but it contains all the information that a competent software developer would need.
Stephen Martin
+2  A: 

If you use the technique in the post referenced by nobugz, you will need to make sure the delegate doesn't get garbage-collected, e.g. by using GC.KeepAlive(_proc) when setting the hook, otherwise after an indeterminate period the hook will stop working when the delagate gets GCed.

Stuart Dunkeld
+6  A: 

None of the provided answers helped me solve my problem, but I found the answer myself. Here it is.

Using SetWindowsHookEx() with WH_KEYBOARD_LL was the correct approach. However, the other parameters to SetWindowsHookEx() are unintuitive:

  • The last parameter, "dwThreadId", needs to be 0.
  • The second-last parameter, "hMod", needs to point to some DLL. I used User32, which is a DLL that is always loaded anyway and is used by all processes with a GUI. I got this idea from a CodeProject post about this.

Thus, the code looks a bit like this:

instance = LoadLibrary("User32");
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, hookFunction, instance, 0);

The documentation is unclear about the second-last parameter. It says "The hMod parameter must be set to NULL [...] if the hook procedure is within the code associated with the current process." It doesn't state that this only applies to some types of hooks, but not to WH_KEYBOARD_LL and WH_MOUSE_LL.

Timwi
Yes it is what they mean - for a low-level mouse or keyboard hook (or any other local hook) hMod should be IntPtr.Zero.
Stephen Martin
Hmmm, you say the other posts weren't helpful then proceed to post the same answer that nobugz already provided and mark yours as the correct answer.
Stephen Martin
Nobugz doesn't say what to do about hMod at all, and contrary to your first comment, this answer suggests hMod to be set to something **other** than IntPtr.Zero.
romkyns
Stephen Martin, irrespective of what you think the authors of the documentation mean, and irrespective of what you think nobugz meant or didn't mean, the fact is my solution solved my problem and his didn't.
Timwi
Nobugz says nothing additional about keyboard hooks because it is unnecessary, it is fully covered in the documentation and the linked blog post. This post and its linked article are incorrect about hMod it is supposed to be NULL. This parameter is currently ignored by the OS...
Stephen Martin
...when the hook is local but that is not guaranteed. The linked article is really of poor quality and it is apparent that the author doesn't really understand hooks at all and just poked around until it worked. Though the blog post that nobugz linked to is rather sloppy itself.
Stephen Martin