Compact Framework: Global Hotkeys


A long time ago, in a galaxy far far away… well… back when I worked in VB.NET up in Dallas… I wrote a little hot key class for compact framework apps and posted a little bit on how to use it. Of course that was all done in VB.NET and I haven’t used that since the end of 2005. Fast forward 5 years and I find myself using the Compact Framework a lot and needing hotkeys again. So, here is the same code translated into C# (with the help of PInvoke.NET):

   1: using System;

   2: using System.Windows.Forms;

   3: using Microsoft.WindowsCE.Forms;

   4: using System.Runtime.InteropServices;

   5:  

   6: namespace CFHotKeys

   7: {

   8:     public class HotKeys

   9:     {

  10:         #region dll imports

  11:  

  12:         [DllImport("coredll.dll")]

  13:         private static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc);

  14:  

  15:         [DllImport("coredll.dll")]

  16:         private static extern bool UnregisterHotKey(IntPtr hWnd, int id);

  17:  

  18:         #endregion

  19:  

  20:         public enum KeyModifiers 

  21:         {

  22:             None = 0,

  23:             Alt = 1,

  24:             Control = 2,

  25:             Shift = 4,

  26:             Windows = 8,

  27:             Modkeyup = 0x1000

  28:         }

  29:  

  30:         public delegate void KeyPressedEventHandler(Keys key);

  31:  

  32:         public event KeyPressedEventHandler KeyPressed;

  33:         private readonly HotKeyMessageWindow wnd;

  34:  

  35:         public HotKeys()

  36:         {

  37:             wnd = new HotKeyMessageWindow(this);

  38:         }

  39:  

  40:         public void Register(Keys Key)

  41:         {

  42:             RegisterHotKey(wnd.Hwnd, (int)Key, 0, (int)Key);

  43:         }

  44:  

  45:         public void Register(Keys Key, KeyModifiers Modifier)

  46:         {

  47:             RegisterHotKey(wnd.Hwnd, (int)Key, (int)Modifier, (int)Key);

  48:         }

  49:  

  50:         public void UnRegister(Keys Key)

  51:         {

  52:             UnregisterHotKey(wnd.Hwnd, (int)Key);

  53:         }

  54:  

  55:         public void OnKeyPressed(Keys key)

  56:         {

  57:             //forward the keypress event to the outside world.

  58:  

  59:             if (KeyPressed != null)

  60:             {

  61:                 KeyPressed(key);

  62:             }

  63:         }

  64:  

  65:         private class HotKeyMessageWindow : MessageWindow

  66:         {

  67:             private const int WM_HOTKEY = 0x312;

  68:             private readonly HotKeys parent;

  69:  

  70:             public HotKeyMessageWindow(HotKeys h)

  71:             {

  72:                 parent = h;

  73:             }

  74:  

  75:             protected override void WndProc(ref Message msg)

  76:             {

  77:                 switch (msg.Msg)

  78:                 {

  79:                     case WM_HOTKEY:

  80:                         {

  81:                             int keyNum = msg.WParam.ToInt32();

  82:                             Keys key = (Keys) keyNum;

  83:                             parent.OnKeyPressed(key);

  84:                             break;

  85:                         }

  86:                     default:

  87:                         {

  88:                             base.WndProc(ref msg);

  89:                             break;

  90:                         }

  91:                 }

  92:             }

  93:         }

  94:     }

  95: }

</div> </div>

You’ll need to add a reference to Microsoft.WindowCE.Forms in your compact framework app.

The hot key interop call technically requires a windows form to receive the hotkey press using a windows message pump handler. This isn’t available in the standard System.Windows.Forms, but it is available in the MessageWindow class in the Microsoft.WindowsCE.Forms assembly. This code instantiates a class that inherits from MessageWindow so that we can handle the hot key press from anywhere in our application, not just from a specific form.

As an example of this being a global hotkey, create a Compact Framework app with a single form in it, and change your Program.cs to look like this:

   1: static class Program

   2: {

   3:  

   4:     private static HotKeys hotKeys;

   5:  

   6:     [MTAThread]

   7:     static void Main()

   8:     {

   9:         hotKeys = new HotKeys();

  10:         hotKeys.Register(Keys.A);

  11:         hotKeys.KeyPressed += hotKeys_KeyPressed;

  12:  

  13:         Application.Run(new Form1());

  14:     }

  15:  

  16:     private static void hotKeys_KeyPressed(Keys key)

  17:     {

  18:         switch (key)

  19:         {

  20:             case Keys.A:

  21:                 {

  22:                     MessageBox.Show("You pressed A!");

  23:                     break;

  24:                 }

  25:             default: break;

  26:         }

  27:     }

  28:  

  29: }

</div> </div>

Anytime you press the “a” or “A” key in the application, you’ll get a message box that pops up. The API for the HotKeys class could be made quite a bit more elegant, IMO. I don’t really like using switch statements like I showed in the program.cs… I would rather register a hotkey with a delegate that gets fired, like this:

   1: private void RegisterHotKeys()

   2: {

   3:     hotKeys.Register(Keys.A, HandleAPress);

   4: }

   5:  

   6: private void HandleAPress()

   7: {

   8:     MessageBox.Show("You Presed A!");

   9: }

</div> </div>

I don’t want to spoil all the fun of playing with this little snippet of code, though. So I’ll let you, the reader, explore the possibilities of implementing this API. 🙂

Coupling Is Your Friend