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