Global Hotkeys With .NET
May 1, 2007
So, last year while working at Resolver Systems I worked with the author of Movable Python, which is a fairly neat application that lets you carry Python around on a USB stick. As an intellectual exercise I reimplemented the core functions in C# with a slightly less eccentric interface (no offence meant, Michael!), but was suprised to find I had to roll my own code to setup global hotkeys to run scripts, which was a nice feature of Movable I wanted to try and add.
The Win32 APIs for this are themselves are pretty grungy (all sorts of nastiness with bitflags and message loops), but I think I’ve been fairly successful in abstracting that away in this simple class, which you can get from here. With this, you can setup a hotkey like this:
Hotkey hk = new Hotkey();
hk.KeyCode = Keys.1;
hk.Windows = true;
hk.Pressed += delegate { Console.WriteLine(“Windows+1 pressed!”); };if (!hk.GetCanRegister(myForm))
{ Console.WriteLine(“Whoops, looks like attempts to register will fail or throw an exception, show an error/visual user feedback”); }
else
{ hk.Register(myForm); }// .. later, at some point
if (hk.Registered)
{ hk.Unregister(); }
Obviously this is a bit of a kitchen sink example to try and show every feature, but even then it’s a damn sight easier than what you would use to set up even a basic usage manually with P/Invoke. If you are wondering about the requirement for a form parameter to Register and GetCanRegister, this is simply a requirement of the underlying APIs, probably due to the fact that they signal hotkey presses in the application message loop rather than via a callback.
I hope someone finds this useful and to facilitate that process I hereby place all the code into the public domain to be messed around with to your hearts content. Enjoy!
May 1, 2007 at 11:07 pm
Eccentric – how dare you. 😉
I’m *more* offended that you haven’t come back to work with us. We even have a customer now. Come and see us sometime…
October 5, 2007 at 4:10 pm
File is 404
I’d like to check out and possibly use your Hotkey class, but it doesn’t appear to be on the site you posted it to originally….do you still have it?
Thanks,
James
October 5, 2007 at 5:04 pm
I’ve now fixed the link: thanks to James for telling me about its breakage!
January 30, 2008 at 5:39 am
nice article.. helpful.
June 16, 2008 at 4:17 pm
I’ve been trying to use this class, it all works well except when I close the app I get a Win32Exception.
I’ve put
if (hk.Registered)
{ hk.Unregister(); }
in the overidden Dispose() method.
Could you give me some suggestions for why this is happening? Thanks.
June 16, 2008 at 5:12 pm
Ok, I finally got it working.
I moved:
if (hk.Registered)
{ hk.Unregister(); }
to the form closing event and seemed to do the trick.
This is a great class BTW, I’ve been looking for a clean way of implementing global hotkeys and this is the best implementation I’ve seen.
July 7, 2008 at 1:06 pm
Great class so far, but I’ve got a problem:
The hotkeys I use in my program don’t affect anything in the other programs any more. No matter whether I set e.Handled in the method for the Pressed event true or false.
Do you have any suggestions how to fix that?
July 7, 2008 at 5:01 pm
@crash:
I’m glad you are finding the class useful!
According to the documentation for RegisterHotKey (http://msdn.microsoft.com/en-us/library/ms911003.aspx):
“””
When a key is pressed, the system looks for a match against all hot keys. Upon finding a match, the system posts the WM_HOTKEY message to the message queue of the thread that registered the hot key. This message is posted to the beginning of the queue so it is removed by the next iteration of the message loop.
“””
So you cannot really have a hot key registered to two applications at the same time, which is what I think you are asking.
July 7, 2008 at 5:10 pm
Ok, I see. Anyway, I’ve just found a way to bypass that problem, so I don’t need that “double hotkeying” any more.
However, thanks for your help.
September 3, 2008 at 4:41 am
Hi there, nice article, the link seems to have gone 404 again though. Any chance you can fix ‘er up?
September 3, 2008 at 9:17 am
Sorry about that: I moved my blog to a new hosting provider and forgot to copy that file.
The link should work now.
September 26, 2008 at 1:04 pm
Hi Max,
Thanks for the code. I thought it would be *much* simpler to implement a global hotkey, but I’m glad I found your implementation before I tried.
I built a task bar application around it that makes the current foreground window transparent when you press a hotkey. It’s called See Through Windows and it’s available here:
http://www.mobzystems.com/tools/seethroughwindows.aspx
Couldn’t have done it without you!
January 12, 2009 at 12:16 am
Thanks! This code helped me out a lot. Not only can I use it in my current project, but I can reference it if I ever need to do interop or message filtering in the future. I was in the same boat as Markus, and I’m extremely happy that I found your code before trying to implement a global hotkey on my own.
I do have one question, though. Why didn’t you use IntPtr.Zero instead of the windows pointer when you invoke RegisterHotKey? Wouldn’t that have avoided the control parameter all together?
January 12, 2009 at 10:06 am
Hi Ryan,
I’m glad you find the code useful! However, I think you are right about using IntPtr.Zero – I must have overlooked that in the RHK documentation.
I don’t have easy access to Visual Studio at the moment, so I’m going to leave the code as it is – I don’t want to upload a buggy version. Unless of course you would like to make the changes and send me the modified version to put up here 🙂
April 22, 2009 at 11:47 am
[…] […]