Friday, March 03, 2006

One of my colleagues recently purchased the Treo 700w and fall in love with it. Being a developer he came up with the idea to try to use the button on the phone's headphones to initiate the Voice Commander. So he approached me with the question on how to place a hook in the WM device in order to listen for all hardware button presses. As a matter of fact, Windows CE does support a keyboard hook through the SetWindowsHookEx API. This API requires the usage of the native callback, which was not possible in the first version of the CF. The current version of .NetCF empowered us with this very useful capability. So what are hooks? Put shortly, a hook is a function that you can create as a part of your application in order to monitor on the messages inside the operating system. Hooks were provided by Microsoft primarily to help developers with the debugging their applications. The incorrect usage of them very easily could bring the system down. Therefore the hooks should be a last resort when all other a more traditional methods don't satisfy your requirents. Below you will find the class HookKeys that wraps all appropriate API calls and exposes all catched messages as a managed HookEvent:

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()

        {

 

        }

 

        #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.");

            }

        }

 

        ///

        /// Stops the hook

        ///

        public void Stop()

        {

            UnhookWindowsHookEx(hHook);

        }

 

        #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 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

 

 

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);  

The HookKeys class should work properly, but there is something missing from this class to be complete. I am not going to tell you what it is. This is going to be a self learning quiz for my readers. You can post the anwers in the comments. I'll anounce the winners in the next post.

3/3/2006 10:32:08 PM (GMT Standard Time, UTC+00:00)  #     |