in Development

Hook Keyboard and Mouse

In the forums of Baleares on .NET (Spanish) a member asked a general question about hooking functions, avoiding the easy joke about what hooking functions are, we could define them basically as a technique which allows putting your own code between the caller and the destination.

One of the easiest and common hooks is the one to capture keyboard and mouse events, something that you can easily implement in .NET making use of InteropServices.

We will see with a sample how to build a class that intercepts the mouse and keyboard messages and raises a .net event, which users can handle (see below to download the full source code).

The first thing is to obtain the functions to install and remove the hooks, which we can find in the user32.dll.

   1: [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
   2: public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, int dwThreadId);
   3:  
   4: [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
   5: public static extern bool UnhookWindowsHookEx(int idHook);
   6:  
   7: [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
   8: public static extern int CallNextHookEx(int idHook, int nCode, IntPtr wParam, IntPtr lParam);

You can find detailed information about what every parameter is in the MSDN, but just as a fast review: idHook is the type of hook to install, lpfn is a pointer to our hook method, hMod is a handle to the DLL that will contain the method pointed by lpfn and finally dwThreadId, which specifies the identifer of the thread with which the hook method is to be associated. The type we have set to lpfn is HookProc, as you will see next, this is just a delegate with the next signature: public delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);

Another important thing to stand up is the third DllImport, what we are going to do is to install and remove hooks from a hook chain, because of that it will be necessary to call the next installed hooks, otherwise we will prevent they are executed.

To install the hook the only thing we need to do is to call SetWiwnodwsHookEx with the rigth parameters.

   1: HookProc mouseHookProc = new HookProc(MouseHookProc);
   2: ...
   3: public void InstallMouseHook()
   4: {
   5:      idHookMouse = NativeMethods.SetWindowsHookEx(NativeConstants.WH_MOUSE_LL, mouseHookProc, Marshal.GetHINSTANCE(this.GetType().Module), 0);
   6:     if (idHookMouse == 0)
   7:         throw new ApplicationException("MouseHook cannot be set");
   8: }
   9: ...
  10: private int MouseHookProc(int nCode, IntPtr wParam, IntPtr lParam)
  11: {
  12:     if (nCode < 0)
  13:     {
  14:         return NativeMethods.CallNextHookEx(NativeConstants.WH_MOUSE_LL, nCode, wParam, lParam);
  15:     }
  16:     else
  17:     {
  18:         MSLLHOOKSTRUCT mouseHookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
  19:  
  20:         HookMouseEventArgs args = new HookMouseEventArgs();
  21:         args.Point = new Point(mouseHookStruct.pt.x, mouseHookStruct.pt.y);
  22:         args.Message = wParam.ToInt32();
  23:  
  24:         OnHookMouse(args);
  25:  
  26:         return NativeMethods.CallNextHookEx(NativeConstants.WH_MOUSE_LL, nCode, wParam, lParam);
  27:     }
  28: }
  29: ...
  30: [StructLayout(LayoutKind.Sequential)]
  31: public class MSLLHOOKSTRUCT
  32: {
  33:     public POINT pt;
  34:     public int mouseData;
  35:     public int flags;
  36:     public int time;
  37:     public int dwExtraInfo;
  38: }

In the excerpt of the above code we set a global hook to intercept all the messages of the mouse. When there is a message the method MouseHookProc is called and we will fire an event with the HookMouseEventArgs class as parameters, after that we take care of call the next hook in the chain. In the line 18 you can see how we convert the pointer lParam received as parameter to an struct of type MSLLHOOKSTRUCT. This class contains some valuable information like “mouseData” that represents the message type (left button down, mouse wheel …), or “pt” that represents the point in which is the mouse.

The last thing you need to know is that you must remove the hook as soon as possible, since it’s resource consuming. This can be accomplished as follows:

   1: public void RemoveMouseHook()
   2: {
   3:     if (idHookMouse > 0)
   4:         NativeMethods.UnhookWindowsHookEx(idHookMouse);
   5:  
   6:     mouseHookProc = null;
   7:     idHookMouse = 0;
   8: }

These are the basis about how to build your own class to handle Mouse Hooks.

I hope you find interesting and remember that you can complete all the information in our bible: the MSDN.