4 minute read

There may come a time when you want to implement some extra features or maybe an Easter egg into your application using keyboard shortcuts. I was recently tasked with creating an Easter egg so we could test some functionality in CodeSmith. After countless searches looking for an elegant solution (I found a few ugly solutions and some nicer ones) I came across Method #3. I’ll First touch on the one I didn’t choose to go with.

Method #1

The following code block will make an unmanaged call to the user32.dll to register the hotkey. I would never go this approach, but if you were like me and couldn’t find anything in the time frame allotted then you might end up implementing this.

First you will need to update your using block.

using System.Runtime.InteropServices;

Then add the following code to your form.

/// <summary> The RegisterHotKey function defines a system-wide hot key </summary>
/// <param name="hWnd">Handle to the window that will receive WM_HOTKEY messages
/// generated by the hot key.</param>
/// <param name="id">Specifies the identifier of the hot key.</param>
/// <param name="fsModifiers">Specifies keys that must be pressed in combination with the key
/// specified by the 'virtualKey' parameter in order to generate the WM_HOTKEY message.</param>
/// <param name="virtualKey">Specifies the virtual-key code of the hot key</param>
/// <returns><c>true</c> if the function succeeds, otherwise <c>false</c></returns>
/// <seealso cref="https://msdn.microsoft.com/en-us/library/ms646309(VS.85).aspx"/>
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint virtualKey);

[DllImport("user32.dll")]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);

public static int MOD_CONTROL = 0x2;
public static int MOD_SHIFT = 0x4;
public static int WM_HOTKEY = 0x312;

private static int keyId;
public static void RegisterHotKey(Form f, Keys key)
{
   int modifiers = 0;

   if ((key & Keys.Control) == Keys.Control)
       modifiers = modifiers | MOD_CONTROL;

   if ((key & Keys.Shift) == Keys.Shift)
       modifiers = modifiers | MOD_SHIFT;

   Keys k = key & ~Keys.Control & ~Keys.Shift;

   Func<bool> ff = () =>
                       {
                           keyId = f.GetHashCode();
                           RegisterHotKey(f.Handle, keyId, (uint) modifiers, (uint) k);

                           return true;
                       };

   if (f.InvokeRequired)
       f.Invoke(ff);
   else
       ff();
}

public static void UnregisterHotKey(Form f)
{
   try
   {
       Func<bool> ff = () =>
                           {
                               UnregisterHotKey(f.Handle, keyId);

                               return true;
                           };

       if (f.InvokeRequired)
           f.Invoke(ff);
       else
           ff();
   }
   catch (Exception ex)
   {
       Debug.WriteLine(ex.ToString());
   }
}

protected override void WndProc(ref Message m)
{
   base.WndProc(ref m);

   if (m.Msg == WM_HOTKEY)
       throw new Exception("You found an Easter Egg.");
}

Then in your Form Load event add the following code.

Keys k = Keys.F2 | Keys.Control;
RegisterHotKey(this, k);

I found this example here, Please note that I did some refactoring to it.

Method #2

I also found this WPF solution that I haven’t tried but should work. This looks like the best approach for implementing this functionality in WPF as I have seen people use the first method.

public WindowMain()
{
   InitializeComponent();

   this.InputBindings.Add(new InputBinding(MyAppCommands.SaveAll, new KeyGesture(Key.F2, ModifierKeys.Control)););

   CommandBinding cb = new CommandBinding(MyAppCommands.SaveAll);
   cb.Executed += SaveAllDocuments;

   this.CommandBindings.Add(cb );
}

private void SaveAllDocuments(object obSender, ExecutedRoutedEventArgs e)
{
}

Method #3

The approach I went I thought was the best way as it is small and elegant piece of code. It is completely safe to implement as it doesn’t have any hooks into native windows dlls. The initial idea came from here, to implement this code just place it anywhere in your Form.

protected override bool ProcessCmdKey(ref Message message, Keys keys)
{
    switch (keys)
    {
        case Keys.F2 | Keys.Control:
            //Process action here.
            return false;
    }

    return false;
}

Method #4

A friend recently sent me this example right before publishing this. I have never seen this approach but never the less it is an example of how to implement this functionality.

CSharp

public class MyMainForm : System.Windows.Forms.Form, IMessageFilter
{
     const int WM_KEYDOWN = 0x100;
     const int WM_KEYUP = 0x101;

     public bool PreFilterMessage(ref Message m)
     {
          Keys keyCode = (Keys)(int)m.WParam & Keys.KeyCode;
          if(m.Msg == WM_KEYDOWN && keyCode == Keys.Escape)
          {
               Console.WriteLine("Ignoring Escape...");
               return true;
          }
          return false;
     }

     private void MyMainForm_Load(object sender, System.EventArgs e)
     {
          Application.AddMessageFilter(this);
     }
}

VB.NET

Public Class MyMainForm
     Inherits System.Windows.Forms.Form
     Implements IMessageFilter

     Private WM_KEYDOWN As Integer = &H100
     Private WM_KEYUP As Integer = &H101

     Public Function PreFilterMessage(ByRef m As Message) As Boolean
          Dim keyCode As Keys = CType(CInt(m.WParam), Keys) And Keys.KeyCode
          If m.Msg = WM_KEYDOWN And keyCode = Keys.Escape Then
               Console.WriteLine("Ignoring Escape...")
               Return True
          End If
          Return False
     End Function 'PreFilterMessage

     Private Sub MyMainForm_Load(sender As Object, e As System.EventArgs)
          Application.AddMessageFilter(Me)
     End Sub 'MyMainForm_Load
End Class 'MyMainForm

Please let me know if you find any issues in the sample code above.

Join the mailing list

Get notified of new posts and related content to your inbox. I will never sell you anything and I will NEVER sell your email address.