- Windows CE Programming - http://www.hjgode.de/wp -

Hooking the keyboard message queue in compact framework code

Here is some nice code to use a keyboard hook implemented in C# for compact framework.

You can use this to catch the funny OS asigned keys like F1, F2, F3, F4, F6 and F7 (Softkey 1 and 2, Phone, End, Volume Up, Volume Down); and last but not least catch the Win Key press.

The hooking class:

using System;
using System.Runtime.InteropServices;
/*
In order to use this class in your program, just declare the varialble and hook up into HookEvent:
HookKeys hook = new HookKeys();
hook.HookEvent += new HookKeys.HookEventHandler(HookEvent);
hook.Start();
*/
public class HookKeys
{
#region delegates
    public delegate int HookProc(int code, IntPtr wParam, IntPtr lParam);
    public delegate void HookEventHandler(HookEventArgs e, KeyBoardInfo keyBoardInfo);
    public HookEventHandler HookEvent;
#endregion
#region fields
    private HookProc hookDeleg;
    private static int hHook = 0;
#endregion

    public HookKeys()
    {
    }
    ~HookKeys(){
        if(hHook!=0)
            this.Stop();
    }
    #region public methods
    ///
    /// Starts the hook
    ///
    public void Start()
    {
        if (hHook != 0)
        {
            //Unhook the previouse one
            this.Stop();
        }
        hookDeleg = new HookProc(HookProcedure);
        hHook = SetWindowsHookEx(WH_KEYBOARD_LL, hookDeleg, GetModuleHandle(null), 0);
        if (hHook == 0)
        {
            throw new SystemException("Failed acquiring of the hook.");
        }
        AllKeys(true);
    }
    ///
    /// Stops the hook
    ///
    public void Stop()
    {
        UnhookWindowsHookEx(hHook);
        AllKeys(false);
    }
    #endregion
    #region protected and private methods
    protected virtual void OnHookEvent(HookEventArgs hookArgs, KeyBoardInfo keyBoardInfo)
    {
        if (HookEvent != null)
        {
            HookEvent(hookArgs, keyBoardInfo);
        }
    }

    private int HookProcedure(int code, IntPtr wParam, IntPtr lParam)
    {
       KBDLLHOOKSTRUCT hookStruct =  (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT));
       if (code < 0)
            return CallNextHookEx(hookDeleg, code, wParam, lParam);
       // Let clients determine what to do
       HookEventArgs e = new HookEventArgs();
       e.Code = code;
       e.wParam = wParam;
       e.lParam = lParam;
       KeyBoardInfo keyInfo = new KeyBoardInfo();
       keyInfo.vkCode = hookStruct.vkCode;
       keyInfo.scanCode = hookStruct.scanCode;
       OnHookEvent(e, keyInfo);
       // Yield to the next hook in the chain
       return CallNextHookEx(hookDeleg, code, wParam, lParam);
   }
   #endregion
   #region P/Invoke declarations

   [DllImport("coredll.dll")]
   private static extern int AllKeys(bool bEnable);

   [DllImport("coredll.dll")]
   private static extern int SetWindowsHookEx(int type, HookProc hookProc, IntPtr hInstance, int m);
   [DllImport("coredll.dll")]
   private static extern IntPtr GetModuleHandle(string mod);
   [DllImport("coredll.dll")]
   private static extern int CallNextHookEx(
           HookProc hhk,
           int nCode,
           IntPtr wParam,
           IntPtr lParam
           );
   [DllImport("coredll.dll")]
   private static extern int GetCurrentThreadId();
   [DllImport("coredll.dll", SetLastError = true)]
   private static extern int UnhookWindowsHookEx(int idHook);
   private struct KBDLLHOOKSTRUCT
   {
       public int vkCode;
       public int scanCode;
       public int flags;
       public int time;
       public IntPtr dwExtraInfo;
   }
   const int WH_KEYBOARD_LL = 20;
   #endregion
}
#region event arguments

    public class HookEventArgs : EventArgs
    {
        public int Code;    // Hook code
        public IntPtr wParam;   // WPARAM argument
        public IntPtr lParam;   // LPARAM argument
    }
    public class KeyBoardInfo
    {
        public int vkCode;
        public int scanCode;
        public int flags;
        public int time;
    }
#endregion

Here is the download of the Visual Studio 2005 Windows Mobile 6 SDK targeting source code:

[Download not found]

Update 20. sept 2019:

Better code which will start/stop the hook with Activate/Deactivate (Show/Minimize) of a form:

using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace HookCEtest
{
    public partial class Form1 : Form
    {
        HookKeys hook;
        public Form1()
        {
            InitializeComponent();
            // Inizialize keyboard hook event handler. Calls "HookEvent" method,
            // when any hardware button is pressed on the field computer.

            hook = new HookKeys();
            hook.HookEvent += new HookKeys.HookEventHandler(HookEvent);
            //hook.Start();
        }

        private void HookEvent(HookEventArgs e, KeyBoardInfo keyBoardInfo)
        {
            /*
            This method is called, when any hardware button is pressed.
            When a defined button is pressed, foreground windows toggles background
            color.
            */
            System.Diagnostics.Debug.WriteLine(String.Format("HookEvent called: vkCode={0}", keyBoardInfo.vkCode));
            if (keyBoardInfo.vkCode == 123 && e.wParam.ToInt32() == 256) // Home button pressed
            {
                System.Diagnostics.Debug.WriteLine("Search button pressed");
                ToggleWindow();
            }

        }
        private void ToggleWindow()
        {
            // Bring window of other software to front
            Color old = this.BackColor;
            this.BackColor = Color.Blue;
            this.Refresh();
            System.Threading.Thread.Sleep(1000);
            this.BackColor = old;
        }

        private void Form1_Activated(object sender, EventArgs e)
        {
            hook.Start();
        }

        private void Form1_Deactivate(object sender, EventArgs e)
        {
            hook.Stop();
        }

    }
}

If you do not use the above, a system thread is stopped when Form is minimized:

'HookCEtest.exe' (Managed): Loaded 'C:\Program Files (x86)\Microsoft.NET\SDK\CompactFramework\v3.5\Debugger\BCL\System.Drawing.dll'
HookEvent called: vkCode=123
HookEvent called: vkCode=123
HookEvent called: vkCode=118
HookEvent called: vkCode=118
HookEvent called: vkCode=123
HookEvent called: vkCode=123
The thread 0xe9ad0012 has exited with code 0 (0x0).

The HookKeys itself does not use any custom thread!