Installing a global hot key with C#

A dude over at Channel 9 Techoff asked about how to install a global hotkey with .NET (Visual Basic .NET); Matthew then pointed him, as a possible way to do it, at the native RegisterHotKey method. All this stuff made me curious and I tried to implement a solution and came up with a quite decent piece of code (I thought it was looking great) that didn’t work and I went to bed.

Now I know it was because I was using a ConsoleApplication project instead of using a WindowsForms application. It seems like as if in the ConsoleApplication the messages that are send to a thread are not forwarded to the windows message pump or are lost somehow else. The implementation of a global hot key relies on the message system because Windows is going to send to the application a message; more exactly the thread that has registered the hot key gets notified via a message. If you are not able to get these messages you will never get any notifications.

Now after switching to a WindowsFormsApplication everything looks fine and works properly.

public sealed class KeyboardHook : IDisposable
{
    // Registers a hot key with Windows.
    [DllImport(“user32.dll”)]
    private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
    // Unregisters the hot key with Windows.
    [DllImport(“user32.dll”)]
    private static extern bool UnregisterHotKey(IntPtr hWnd, int id);

    /// <summary>
    /// Represents the window that is used internally to get the messages.
    /// </summary>
    private class Window : NativeWindow, IDisposable
    {
        private static int WM_HOTKEY = 0×0312;

        public Window()
        {
            // create the handle for the window.
            this.CreateHandle(new CreateParams());
        }

        /// <summary>
        /// Overridden to get the notifications.
        /// </summary>
        /// <param name="m"></param>
        protected override void WndProc(ref Message m)
        {
            base.WndProc(ref m);

            // check if we got a hot key pressed.
            if (m.Msg == WM_HOTKEY)
            {
                // get the keys.
                Keys key = (Keys)(((int)m.LParam >> 16) & 0xFFFF);
                ModifierKeys modifier = (ModifierKeys)((int)m.LParam & 0xFFFF);

                // invoke the event to notify the parent.
                if (KeyPressed != null)
                    KeyPressed(this, new KeyPressedEventArgs(modifier, key));
            }
        }

        public event EventHandler<KeyPressedEventArgs> KeyPressed;

        #region IDisposable Members

        public void Dispose()
        {
            this.DestroyHandle();
        }

        #endregion
    }

    private Window _window = new Window();
    private int _currentId;

    public KeyboardHook()
    {
        // register the event of the inner native window.
        _window.KeyPressed += delegate(object sender, KeyPressedEventArgs args)
        {
            if (KeyPressed != null)
                KeyPressed(this, args);
        };
    }

    /// <summary>
    /// Registers a hot key in the system.
    /// </summary>
    /// <param name="modifier">The modifiers that are associated with the hot key.</param>
    /// <param name="key">The key itself that is associated with the hot key.</param>
    public void RegisterHotKey(ModifierKeys modifier, Keys key)
    {
        // increment the counter.
        _currentId = _currentId + 1;

        // register the hot key.
        if (!RegisterHotKey(_window.Handle, _currentId, (uint)modifier, (uint)key))
            throw new InvalidOperationException(“Couldn’t register the hot key.”);
    }

    /// <summary>
    /// A hot key has been pressed.
    /// </summary>
    public event EventHandler<KeyPressedEventArgs> KeyPressed;

    #region IDisposable Members

    public void Dispose()
    {
        // unregister all the registered hot keys.
        for (int i = _currentId; i > 0; i–)
        {
            UnregisterHotKey(_window.Handle, i);
        }

        // dispose the inner native window.
        _window.Dispose();
    }

    #endregion
}

/// <summary>
/// Event Args for the event that is fired after the hot key has been pressed.
/// </summary>
public class KeyPressedEventArgs : EventArgs
{
    private ModifierKeys _modifier;
    private Keys _key;

    internal KeyPressedEventArgs(ModifierKeys modifier, Keys key)
    {
        _modifier = modifier;
        _key = key;
    }

    public ModifierKeys Modifier
    {
        get { return _modifier; }
    }

    public Keys Key
    {
        get { return _key; }
    }
}

/// <summary>
/// The enumeration of possible modifiers.
/// </summary>
[Flags]
public enum ModifierKeys : uint
{
    Alt = 1,
    Control = 2,
    Shift = 4,
    Win = 8
}

There are a few points of interest in this code: one is that I used a so called NativeWindow. The NativeWindow class provides a very basic Win32 window. It’s up to you what you want this window to be. Everything needs to be specified, like the handle needs to be created manually etc. That adds very little overhead if you only need a handle to register a window for a callback and a WndProc to get the message.

Another interesting point is that you can register multiple modifiers with for the hot key. That is why I have marked the modifier keys’ enumeration with the FlagsAttribute. This attribute is always used when you can specify not just one enumeration item but a combination of them.

Each time the hot key has been pressed I fire an event from inside the native window. That event is than re-fired by the KeyboardHook class. I could have solved this problem in another way, for example, by giving the NativeWindow instance the KeyboardHook instance but that would just add another layer of connection between the two classes.

The usage of the class looks like this:

public partial class Form1 : Form
{
    KeyboardHook hook = new KeyboardHook();

    public Form1()
    {
        InitializeComponent();

        // register the event that is fired after the key press.
        hook.KeyPressed +=
            new EventHandler<KeyPressedEventArgs>(hook_KeyPressed);
        // register the control + alt + F12 combination as hot key.
        hook.RegisterHotKey(ModifierKeys.Control | ModifierKeys.Alt,
            Keys.F12);
    }

    void hook_KeyPressed(object sender, KeyPressedEventArgs e)
    {
        // show the keys pressed in a label.
        label1.Text = e.Modifier.ToString() + ” + “ + e.Key.ToString();
    }
}

Published on Feb 2nd, 2008 — Tags: ,
   digg it!    kick it   

57 Comments ( Comments RSS TrackBack )

  1. Nice encapsulation!
    Just what I needed to avoid losing my KeyDown events when the WebBrowser control has the focus. Even with KeyPreview = True I was losing them.
    Using your nicely designed code, I was able to catch my Shortcut keys all the time, without cluttering my code with API calls and WndProc event handlers!
    Thanks
    Frederick

    Comment by Frederick — February 13, 2008 @ 9:56 pm

  2. In the dispose function you have “for (int i = _currentId; i > 0; i–)”. This is missing the second minus sign.

    Comment by Patrick — July 17, 2008 @ 10:00 pm

  3. This is something that the blog engine rips away… I’m sorry for it but I can’t change it :(

    Comment by Christian Liensberger — July 18, 2008 @ 12:01 am

  4. Your encapsulation was the best one on Google I’ve tried. Works nice and is well-designed. Most others just look like hacks.

    You should publish on CodePlex or Google Code or CodeProject so it’s easier to find and I can send you some patches too :-)

    And as with wordpress, I’d suggest you just upload the source file somewhere and links to it, that should do the trick.

    Thanks for sharing and taking the time to wraps it up in a class :-) Saved me a lot of hours.

    Comment by chakrit — September 12, 2008 @ 11:20 am

  5. Thanks for this feedback. I will probably attach the source file when I publish other snippets.

    Comment by Christian Liensberger — September 12, 2008 @ 10:48 pm

  6. Is it possible to catch “ctrl alt del” wenn using “ctrl alt del” as hotkey and do nothing in my code?
    Does it run on vista too?

    Comment by Tom — September 17, 2008 @ 4:50 am

  7. hook.RegisterHotKey(Oesd.KeyLock.Communication.ModifierKeys.Control | Oesd.KeyLock.Communication.ModifierKeys.Alt, Keys.Delete);

    It does not work (on windows XP SP3)
    // register the hot key.
    if (!RegisterHotKey(_window.Handle, _currentId, (uint)modifier, (uint)key))
    throw new InvalidOperationException(”Couldn’t register the hot key.”);

    How can i catch “ctrl alt del”?

    Comment by Tom — September 17, 2008 @ 5:06 am

  8. I don’t think that you can catch CTRL + ALT + DELETE. It’s a special combination of keys and they probably get eaten at the highest level in the OS to make sure that apps can’t hinder the user from pressing them and getting the results that are expected by these special keys.

    Comment by Christian Liensberger — September 25, 2008 @ 5:17 am

  9. Hi
    A have some problem with this instruction : hook.RegisterHotKey(ModifierKeys.Control | ModifierKeys.Alt, Keys.F12);
    When i’m entering ModifierKeys i dont see Control … I’m missing something ? Please help me solve this problem.

    Comment by Kamil — December 15, 2008 @ 4:30 pm

  10. public enum ModifierKeys : uint
    {
    Alt = 1,
    Control = 2,
    Shift = 4,
    Win = 8
    }

    I see it..! :)

    Comment by Heikki — January 9, 2009 @ 2:10 am

  11. Can this be make to work with WPF applications. Tried to work it out, but RegisterHotKey always returns false.
    Can you please help me solve this problem ?

    Thanks,
    Vivek

    Comment by vivek — February 12, 2009 @ 12:38 pm

  12. Actually it works well if I just use Ctrl instead of both Alt and Ctrl

    Thanks

    Comment by vivek — February 13, 2009 @ 1:16 am

  13. hello,
    first of all thank for the code.
    i have the same problem like post #9 by kamil, i can’t see the enumeration while it’s included in the source…
    i’ve added a new class, same namespace like my project and copied the code (and fixed the ” and — stuff). i have no idea, why i can’t use the enumeration? any hint? thanks!

    Comment by abc — May 19, 2009 @ 5:54 am

  14. I had to use it like so:

    hook.RegisterHotKey((ModifierKeys)2|(ModifierKeys)1, Keys.N);
    for control+alt+N

    for those who have a problem with the ModifierKeys.Control syntax like I did.

    Comment by viemort — May 20, 2009 @ 9:20 pm

  15. Thanks for this, What I couldn’t find out was how to get the key info from the message, and in your code was just what I needed. I wrote just a simple class for registering hotkeys, keeping track of them using native window, and a custom class inherited from eventargs thatheld my modifiers enum and a key enum. passed them through a custom event. then in the event handler I just use an if to check for the same keys.

    Works great and is pretty easy to use, but what I can’t seem to figure out is to set this up to be able to be created dynamically.

    But I guess thats next for me.

    Comment by Rickey — May 22, 2009 @ 7:24 pm

  16. I had the same issue for the enumeration hiding.
    1. I added My project name space int he using lines. (i.e., stealthmode)
    and called the enumeration like StealthMode.ModifierKeys.Control

    it was working.

    hope its help full for others too.

    Comment by Azmat — June 8, 2009 @ 6:14 am

  17. Excellent. Thank you.

    Comment by tim — June 22, 2009 @ 11:11 am

  18. Hi, is that ModifierKeys required? Can I create a global hotkey with just “F12″ - more quickly :-) Thanks for this great stuff.

    Comment by Quynh — July 26, 2009 @ 8:35 pm

  19. Thank you very much for this extremely well done implementation.

    One thing I was wondering about: I’m currently trying to temporarily disable keyboard input in my application. I tried System Hooks with win32 API but that doesn’t work stable and seams to vary in behavior across different windows derivates. Using your code, it seems to work stable on any system I tested so far. So I was just wondering, whether the method you apply could also be used for disabling the keyboard. Actually it would be sufficient to disable any control keys like “alt, ctrl, win, enter, esc etc).

    What do you think?

    THX

    Comment by Michael — July 31, 2009 @ 2:27 am

  20. Very nice, that helped me a lot. Thank you for publishing!

    Comment by Milan — August 11, 2009 @ 8:09 am

  21. Wow , very useful , thanks

    Comment by charlie — August 14, 2009 @ 1:44 pm

  22. You are the best.
    Fantastic Stuff.

    Comment by Ronald — August 25, 2009 @ 9:45 pm

  23. Thank you!!! You save me a lot of time!

    Comment by Olex — September 12, 2009 @ 11:31 pm

  24. One issue I am facing with RegisterHotKey is that the Windows message does not get passed to the acive App . I have registered Ctrl+S as Save within my app using RegisterHotKey. If I open a notepad app and type something and hit Ctrl+S, the code in my app is called and the NotePad file is not saved. I have not found a way to pass the message back into the MessageLoop so that NotePad can handle it too.

    Unless I have misunderstood something, RegisterHotKey is a strict SystemWide things and cannot be modified for a Application level handling.

    Comment by Saravanan — October 6, 2009 @ 7:45 am

  25. Thank you Christian. It helped me a lot.

    Comment by Pablushka! — January 25, 2010 @ 1:03 pm

  26. Thanks a lot. That was very helpful.

    Comment by Farooq Azam — June 1, 2010 @ 10:56 am

  27. Awesome!
    Just what I needed, thanks for sharing the code :)

    Comment by Marcus — June 2, 2010 @ 10:50 pm

  28. Works great in studio, however does not execute after publish on same machine. ??

    Comment by MikeTM — June 29, 2010 @ 12:34 pm

  29. Great work i must say. Keep it up. I was looking something to have one key clicked rather than a combination of keys but this might work too,

    if you have anything that work with only one key like pressing F9 or F12 will be great to have it

    thanks

    Comment by Sidd — July 29, 2010 @ 7:45 am

  30. Thank you greatly for this!

    @Sidd
    Add None = 0 to the ControlModifiers Enum

    Like this

    public enum ModifierKeys : uint
    {
    None = 0,
    Alt = 1,
    Control = 2,
    Shift = 4,
    Win = 8
    }

    And then use it like this

    RegisterHotKey(ModifierKeys.None, Keys.F9);

    Comment by Dreed — August 4, 2010 @ 5:19 pm

  31. Is there a way to do this so that the key does not get blocked in other apps? My app needs a hotkey for start/stop function because it is usually run behind a full screen app. The full screen app should still be able to use the key as well as triggering the start/stop in my app.

    Comment by Gavin — August 7, 2010 @ 8:00 pm

  32. fantastic man !
    many thanks !

    Comment by bedorlan — October 8, 2010 @ 3:28 pm

  33. Hi,

    I used this sample with my Visual Studio 2010 web application. But it is not firing the event when I pressed ctrl+alt+F12. I tried with some other key combinations and it is not working. What might be the issue? Please help.

    Also, most often I get error message “Could n’t register Hot Key”.

    Comment by Pitchai — October 12, 2010 @ 4:50 am

  34. [...] http://www.liensberger.it/web/blog/?p=207 Al?nt? Yaparak Yan?tla + Cevap Yaz « Tcp S?n?flar? ile Ayn? exe ?zerinden s?rekli veri almak ve veri g?ndermek ? [...]

    Pingback by Herhangi bir tu?un bas?lmas?n? engelleme — March 3, 2011 @ 6:25 pm

  35. Very helpful, thanks!

    Comment by Webdiyer — November 10, 2011 @ 6:43 am

  36. hi Professionals,
    This is one of the worst idea.
    because if we used this method and register our own hotkey means then other applications hotkeys are not work.

    For example:
    1) Register Ctrl + S as hotkey in your application for doing something and run your application,
    2) Now open a notepad and type something and try to save the file using Ctrl + S, ohoooo not saving.
    3) Look at your application the hot key works very good for your application not for the currently active notpad.

    Comment by Justin Diraviam — January 20, 2012 @ 5:07 am

  37. I used this sample with my Visual Studio 2010 web application. But it is not firing the event when I pressed ctrl+alt+F12. I tried with some other key combinations and it is not working. What might be the issue? Please help.

    Comment by yanjing — June 19, 2012 @ 8:01 pm

  38. Very good snippet! I tried several things before, but this code just works. Thank you.

    Comment by Harry Blauberg — July 22, 2012 @ 10:20 am

  39. For those which have problems with ModifierKeys enum, i found the problem.
    It’s because “ModifierKeys” already exist in the Windows.Form class.
    You can simply rename the enum, for example : “ModKeys” and modify all occurences on the KeyboardHook class. And now you can use ModKeys.Alt or others, it works.

    ps : sorry for my bad english

    Comment by Selven — September 4, 2012 @ 3:16 am

  40. Hey, Thanks a lot, this really helped! Cheers.

    Comment by Abijeet — October 27, 2012 @ 11:32 am

  41. This is pure gold. The stuff described is above my level, but I learned a lot while implementing the functionality into my project.

    Comment by Anders Frey — December 8, 2012 @ 4:38 pm

  42. Can I use this code in a commercial application?

    Did the source ever get posted on a site?

    Comment by Blake Robertson — December 28, 2012 @ 5:41 pm

  43. I posted this code as a gist here:
    https://gist.github.com/4404150

    You can download it or copy and paste it right into an editor without having to make modifications.

    Thanks for the excellent class Christian.

    Comment by Blake Robertson — December 28, 2012 @ 7:40 pm

  44. Can you clarify what terms you are releasing this code under (GPL, MIT, Public Domain, etc.)? It’s difficult to decide if I can legally reuse this code without a published license.

    Comment by Chris — March 22, 2013 @ 2:13 pm

  45. Thank you so much for creating this. You just saved a lot of my time. I loved how clean the example was and that it is ready to use right away. Respect!

    Comment by Jouni Miikki — March 29, 2013 @ 2:11 am

  46. 9 million of operating income, which has made significant
    strides. Information for evaluating energy usage, conservation, new and
    Renewable Uk Jobs energy, about two-thirds of which comes from hydro
    power. But, given the scope and interest of investors,
    the state of Colorado has48, 000 fracked wells, with no other
    questions, we appreciate everybody coming on
    today. The minister further said that the alternative source of energy is consideredbaseload powerthat can operate 24 hours per day, 365 days
    per year.

    Comment by thebigchoice.com — May 26, 2013 @ 5:14 am

  47. If you are using namespaces (more than likely), you may need to apply the namespace in which the enum ‘ModifierKeys’ is in. For example… hook.RegisterHotKey(YOUR_NAMESPACE.ModifierKeys.Control, Keys.F12);

    The class System.Windows.Forms.Form contains ModifierKeys too. However I cannot find any documentation about this so it is likely my facts are sightly incorrect but it was my findings anyway.

    Comment by Jean-Luc Tallis — September 27, 2013 @ 8:25 pm

  48. I like the valuable info you provide in your articles.
    I’ll bookmark your weblog and check again here regularly.
    I am quite sure I will learn lots of new stuff right here!
    Good luck for the next!

    Comment by Rebbeca — February 23, 2014 @ 3:57 am

  49. Hi, I do think this is an excellent web site.

    I stumbledupon it ;) I am going to return once again since I book marked
    it. Money and freedom is the best way to change, may you
    be rich and continue to guide other people.

    Comment by Barrie — March 27, 2014 @ 8:41 am

  50. After checking out a few of the blog posts on your
    web page, I really appreciate your technique of blogging.
    I saved it to my bookmark site list and will be checking back in the near future.
    Please check out my website as well and let me know what you think.

    my blog post; Brave Frontier Cheats, Audra,

    Comment by Audra — May 3, 2014 @ 10:38 pm

  51. It’s going to be ending of mine day, except before end I am reading
    this wonderful piece of writing to increase my
    experience.

    Comment by Athena — June 7, 2014 @ 2:09 am

  52. That’s if half of your visitors will click on those ads, and if more ‘.
    You should consider becoming an infopreneur and share your knowledge with the rest of the world, and
    get paid to do it. If you don’t have a strong opinion, and you can’t express it clearly and convincingly, nobody will take the time to
    listen to you online.

    my site - eFormula Evolution legit

    Comment by eFormula Evolution legit — June 9, 2014 @ 1:27 am

  53. What’s up, the whole thing is going well here and ofcourse every one is sharing information, that’s really fine, keep up writing.

    Comment by Ina — June 26, 2014 @ 10:54 pm

  54. What’s Going down i am new to this, I stumbled upon this I have
    discovered It positively helpful and it has aided me out loads.
    I am hoping to contribute & aid other users like its helped me.

    Great job.

    Comment by Final Fantasy 14 Hacks,Final Fantasy XIV hacks,Final Fantasy 14 Guide — June 30, 2014 @ 8:54 pm

  55. That is very interesting, You aree an excessively
    professional blogger. I have joined your feed and
    sit upp for in search of more oof your fantastic post. Additionally, I’ve shared your website in my
    social networks

    Comment by ccirms.com — July 6, 2014 @ 2:22 am

  56. Thanks for finzlly talking about > Christian Liensberger ? Installing
    a global hot key with C# < Liked it!

    Comment by Olga Olsson online — July 10, 2014 @ 5:32 pm

  57. Hello i am kavin, its my first occasion to commenting anywhere, when i
    read this piece of writing i thought i could also make comment due to this brilliant paragraph.

    Comment by Final Fantasy 14 Hacks — July 16, 2014 @ 4:40 am

Leave a comment

You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> [code][/code] [code lang="csharp"][/code].