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!

15 Responses to “Global Hotkeys With .NET”

  1. Fuzzyman Says:

    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…

  2. James Says:

    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

  3. Max Says:

    I’ve now fixed the link: thanks to James for telling me about its breakage!

  4. AB Says:

    nice article.. helpful.

  5. Ketan Says:

    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.

  6. Ketan Says:

    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.

  7. crash Says:

    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?

  8. Max Says:

    @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.

  9. crash Says:

    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.

  10. Yubs Says:

    Hi there, nice article, the link seems to have gone 404 again though. Any chance you can fix ‘er up?

  11. Max Says:

    Sorry about that: I moved my blog to a new hosting provider and forgot to copy that file.

    The link should work now.

  12. Markus The Says:

    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!

  13. Ryan Says:

    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?

  14. Max Says:

    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 :-)


Comments are closed.

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: