Low-Level Mouse Hook in C#


After my last post on implementing low-level keyboard hooks in C#, Soumitra asked if it was possible to implement a low-level mouse hook in C#, too.  Sure.  Here is an example that will print out the location of the mouse every time you press the left mouse button down:

class InterceptMouse
{
private static LowLevelMouseProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;

public static void Main()
{
_hookID = SetHook(_proc);
Application.Run();
UnhookWindowsHookEx(_hookID);
}

private static IntPtr SetHook(LowLevelMouseProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_MOUSE_LL, proc,
GetModuleHandle(curModule.ModuleName), 0);
}
}

private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);

private static IntPtr HookCallback(
int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 &&
MouseMessages.WM_LBUTTONDOWN == (MouseMessages)wParam)
{
MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
Console.WriteLine(hookStruct.pt.x + “, ” + hookStruct.pt.y);
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}

private const int WH_MOUSE_LL = 14;

private enum MouseMessages
{
WM_LBUTTONDOWN = 0x0201,
WM_LBUTTONUP = 0x0202,
WM_MOUSEMOVE = 0x0200,
WM_MOUSEWHEEL = 0x020A,
WM_RBUTTONDOWN = 0x0204,
WM_RBUTTONUP = 0x0205
}

[StructLayout(LayoutKind.Sequential)]
private struct POINT
{
public int x;
public int y;
}

[StructLayout(LayoutKind.Sequential)]
private struct MSLLHOOKSTRUCT
{
public POINT pt;
public uint mouseData;
public uint flags;
public uint time;
public IntPtr dwExtraInfo;
}

[DllImport(“user32.dll”, CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId);

[DllImport(“user32.dll”, CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);

[DllImport(“user32.dll”, CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
IntPtr wParam, IntPtr lParam);

[DllImport(“kernel32.dll”, CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
}

Comments (67)

  1. Joe says:

    I am getting two compiler errors when attempting to run this code:

    private static LowLevelMouseProc _proc = HookCallback; -> MouseHook.HookCallback(int, System.IntPtr, System.IntPtr)’ referenced without parentheses

    if (nCode >= 0 && MouseMessages.WM_LBUTTONDOWN == (MouseMessages)wParam) -> MouseHook.cs(86): Cannot convert type ‘System.IntPtr’ to ‘MouseMessages’

    Please email joe@jtasoftware.com if you see a way around this.  Thank you.  I am using VS 2003.

  2. toub says:

    That code is using delegate inference, a feature in C# 2.0/Visual Studio 2005.  If you’re using C# 1.x/VS2003, just change:

       HookCallback

    to be:

       new LowLevelMouseProc(HookCallback)

  3. toub says:

    Oh, and to fix the second issue if you’re using a version of C# prior to 2.0, you can change that usage of wParam to be wParam.ToInt32(), and that should fix it.

  4. Randy Smart says:

    Hi Stephen, does the Low Level Mouse hook code work with c# express edition.

    I cant seem to get it to work.

    I am new to c#.

  5. Murtaza says:

    Hello,

    How can this class be used for controlling remote  System’s desktop.

    Regards,

  6. Jamie says:

    Would there be a way of controlling the mouse pointer with similar calls?  I’ve attempted to modify the values before the CallNextHookEx with no joy.

    Jamie.

  7. bob says:

    This code doesnt work with visual c# net2.0

    Makes the application bug and crach

    the CallBack Handler doesn’t give anything =(

  8. fido says:

    hello stephen

    i want you to provide me with a code that enable’s me to monitor all the processes on my PC using C#.

    thank you.

  9. toub says:

    You can use the System.Diagnostics.Process class to get information about processes on your machine.

  10. Andy.Tao says:

    Why it it so slow when I click left button in the same application console window?

  11. Anish says:

    How can we change this code to generate mouse click and double click events?

    I can generate a mouse click event on MouseDown event. But I dont know how to generate double click event.

  12. Igor Gatis says:

    I’m using your code to launch an very simple app that allows to copy code snippets from anywhere. It works just fine until I right click on the app tray icon to access its menu. After that, it does fire keyboard events to my app anymore (like it was unhooked).

    Is it expected? Or am I doing something that’s causing this?

  13. Igor Gatis says:

    Sorry, this was supposed to be posted in the keyboard post.

  14. Edard Schoenherr says:

    I was wondering how to block mouse movements for a period of time and then allow them again.  I.E. if you were writing a script program that interfaces with an application, where the user can click on a dialog box that I create, which will in turn cause my script tto click on things and send keyboard events to a different application – so, once the script is clicking on the application, it would need the mouse movements to be ignored, so the user doesn’t move the mouse out of the location that the script is trying to click on.  This would be a very quick off-on and I could possibly just use GetCursorPos – SetCursorPos, but I am trying to avoid the 1/1000000 chance that tthe user still move the mouse fst enough to break the script.

  15. Ferg says:

    Mouse hooks and performance?

    I’ve implemented your mouse hook to get mouse events in a Word Add-In. It works great – the mouse messages are coming through loud and clear! One thing I’ve found though, is that the impact on performance is very noticeable (especially when selecting a large body of text.) Are you aware of any performance tweaks for windows hooks? I’m really only interested in WM_MOUSEMOVE messages as I want to simulate mouseovers, but of course these are the most verbose by a long shot.

  16. Fritz says:

    Interesting code, but i can’t get it to work as i hoped…

    Can someone give me a working code example for this mousehook;

    Example, user clicks anywhere on the screen and a messagebox pops up indicating if left or right mousebutton was pressed.

  17. petros says:

    great stuff, great work, great example

    how can i hook all windows messages? all i Know is that i have to use  WH_GETMESSAGE

  18. Calin says:

    Hi,

    How can I get the current module handle in HookCallback?

    Thank you,

    Calin

  19. Stop Mouse Left Key Bringing form to top says:

    I need to stop my form coming to the top when left mouse button is pressed. In other words, stop, all together, this mouse event from arriving at the form.

    Many thanks

  20. Dan says:

    when i run this code on a win2k3 box over remote desktop the mouse movements are recorded correctly but the mouse down and up are recored as WM_MOUSEMOVE where as on vista it is recorded correctly as WM_LBUTTONDOWN and then WM_LBUTTONUP

  21. nobelo says:

    Hello,

    if you click left slows down the mouse, application remains to hang!! Did you already see that?

  22. DotNetTestMonkey says:

    This example and the keyboard hooks example work fine.  Thanks for the info.

  23. Mike says:

    There is a post above about blocking mouse movement.  I’d like to do that too.  I need to block mouse movement onto one of the several display devices that make up the desktop.  Do I hook the mouse movements and then eat the messages?  Is there anyway to eat those messages after you’ve hooked them?

    Thanks!

  24. xp says:

    Thanks for the code… excellent foundation for my sweet mouse-handling class.

  25. asnat says:

    great solution

    i need some extra help on getting mouse selected text from any application to my clipboard

    i would appriciate any help on the subject

    thanks asnat

  26. Manoj K says:

    I have implemented mouse hook to get mouse events in a Word Add-In using above code. It is working fine but sometimes I am getting below Exception:

    "AccessViolationException :

    Attempted to read or write protected memory. This is often an indication that other memory is corrupt."

    Whenever this exception occers word stops responding and here I’m not able kill the exception.

    any idea or clues?

  27. Nasir Khan says:

    Wanted to add something to what Manoj K is trying to address.

    We have given Smart UI effect to custom schema part/content control in Word Content area.

    Whenever we try to bring Smart UI for few  content control/custom elements, we get the AccessViolationException.

    Message : "AccessViolationException : Attempted to read or write protected memory. This is often an indication that other memory is corrupt."

    Due to this exception the Tread for SetWindowsHookEx method is alos going into halt state. It is crashing word.

    Thanks and Regards,

    Nasir Khan

  28. minhvc says:

    After set Low-Level Mouse, mouse click on button Minimize or Maximinze, the GUI sized more slow (delay serveral)than UnHook, how to seedup?

  29. stojakovich says:

    how about hooking api of CreateProcess or ExecuteProcess?

    Thanks,

    Earl

  30. yosi says:

    i need a code to add a new item to the pop-up menu of the computer(right click on the mouse)

    thanks.

  31. fazal says:

    man can you give me a solution to hook keyboard from a windows service.

  32. Chris says:

    Well, it seems that the problem is not with the windows service itself.  I’ve read many have managed to circumvent that issue by checking "Allow Desktop Interaction" or something along those lines.  The problem I’m having, however, is that Vista won’t really allow this.  So, I’m looking for a similar solution that works under vista as a windows service…

  33. Nate says:

    I wrote code that works perfect but as soon as I run it under a windows service the event never gets handled. My service runs under Local System and has Allow interact with desktop checked; I have also tried unchecking it with no results. I can step through the code and make sure the handler gets resgitered with += and I can see the line of code being executed but no keystrokes get logged. Has anyone been able to run keyboard hook under a windows ervice?. Is as if the event never gets fired. Please help!

  34. Donovan says:

    Can this be altered to capture the filename of the process that raised the event?

  35. Donovan says:

    Sorry, allow me to be more clear. I am looking to write an app to help with a spyware problem. Basically, when i click on that "You have a serious virus" balloon, i want to get the filename of the program running it. Then go into safemode and wipe it from the system32 folder. It will bridge the cap between what i can clearly see is spyware, and what avg does not yet see as spyware.

    Many thanks!

  36. Benjamin says:

    Hi Stephen Toub

    Thank you very much for these interceptors. They practically saved my ass :)

  37. contactsrinivasan81 says:

    Toub,

          we used ur low level mouse hook code and it works fine.In our case when ever user right click and select the copy option we need to do some action.how to find it.Is there any way to find it.Please help us.

    Thanks in advance.

  38. sunitha says:

    how to dispose mouse hook on application close

  39. Prasad says:

    Please explain How to raise Mouce click hook

  40. Hos says:

    Hi,

    I was trying to run this piece of code on Windows Mobile 6.1.

    I replaced

    using (ProcessModule curModule = curProcess.MainModule) …

    with

    return SetWindowsHookEx(WH_MOUSE_LL, proc, curProcess.MainWindowHandle, 0);

    I got it compiling but it does not seem to be functioning. Any ideas?

    Thanks.

  41. Nameless says:

    IF you want more help for C# development …

    Visit this …

    http://mycsharpdotnet.blogspot.com/

  42. Гносис says:

    How to eat key strokes?

    For example, i need to clear key buffer, to avoid print space, when i press WINKEY + SPACE, and perform some operation…

    private static IntPtr HookCallback(

       int nCode, IntPtr wParam, IntPtr lParam)

    {

      int vkCode = Marshal.ReadInt32(lParam);

      if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)

      {

        if ((Keys)vkCode == Keys.LWin ||

            (Keys)vkCode == Keys.RWin)

        {

          WinKeyStillPressed = true;

        }

      }

      if (nCode >= 0 && wParam == (IntPtr)WM_KEYUP)

     {

       if ((Keys)vkCode == Keys.LWin ||

           (Keys)vkCode == Keys.RWin)

       { //clear flag

         WinKeyStillPressed = false;

       }

       if (WinKeyStillPressed)

       {

         if ((Keys)vkCode == Keys.Space)

         {

           PlayPauseInner(); // My Styff

           return CallNextHookEx((IntPtr)0, 0, wParam, (IntPtr)0); // does not eat key strokes

           return (IntPtr)0; // does not eat key strokes

           return (IntPtr)1; // does not eat key strokes

         }

         if ((Keys)vkCode == Keys.Left)

         {

           PlayRewind(); // My Styff

         }

         if ((Keys)vkCode == Keys.Right)

         {

           PlayForward(); // My Styff

         }

       }

     }

     return CallNextHookEx((IntPtr)0, 0, wParam, (IntPtr)0);

    }

  43. Some Schmoe says:

    Code like this is great… THANK YOU.

    However, it would be nice if you included the ‘using’ clauses for those of us that don’t know the ins and outs of every piece of the .NET framework…

    Looks like this needs these:

    using System;

    using System.Collections.Generic;

    using System.Runtime.InteropServices;

    Feel free to correct me if I guessed wrong :)

  44. Bloodyaugust says:

    Also, it needs:

    using System.Diagnostics;

    :)

  45. lqx says:

    I want to know, when i selected some files with mouse, how to get the file names in the other application??

  46. damien says:

    with this code, you can detect the mouseWheel scrolling, but how do i detect whether its scrolling upwards or downwards? i need something like MouseEventArgs right? how do i insert this into the code above?!

  47. paolo says:

    hi, these 4:

    using System;

    using System.Collections.Generic;

    using System.Runtime.InteropServices;

    using System.Diagnostics;

    don't seem to be enough

    I got this:

    Error 1

    The name 'Application' does not exist in the current context

  48. paolo says:

    oh, well, "Application" is in System.Windows.Forms

  49. Runaurufu says:

    @damien just check hookStruct.mouseData (fromHookCallback) value, it differs when you move upward and downward with mouse scroll :p

  50. Johnny says:

    It seems that the WH_MOUSE_LL hook doesn't intercept mouse events applied on a ribbon. Is there a solution?

  51. Johnny says:

    My mistake, it works well, even with ribbon applications.

  52. Evgeni says:

    Hi,

    I have a USB-Mouse and a PS/2-Mouse attached at the same time. Is it possible to distinguish which of the two mice has triggered the hookcallback?

    Or is it possible two have a hoocallback for only one of the two mice?

    Thanks

  53. TweaK says:

    Does anyone have a working example of this in a Solution??

    This LowLevel stuff is new to me and i don't see how to get it working..

    Everything compiles and runs, but the Output window remains empty 😡

  54. ken says:

    i converted the C# code to vb net ith one of those tools.

    I have problem with this line

    Private Shared _proc As LowLevelMouseProc = HookCallback()  

    it wants the parameters,

    what should they be???

    Ken  

    vb net 2008 below code

    Class InterceptMouse

       'imports System;

       'imports System.Collections.Generic;

       'imports System.Runtime.InteropServices;

       'imports System.Diagnostics;

       'imports System.Windows.Forms

       Private Shared _proc As LowLevelMouseProc = HookCallback()

       Private Shared _hookID As IntPtr = IntPtr.Zero

       Public Shared Sub Main()

           _hookID = SetHook(_proc)

           Application.Run()

           UnhookWindowsHookEx(_hookID)

       End Sub

       Private Shared Function SetHook(ByVal proc As LowLevelMouseProc) As IntPtr

           Using curProcess As Process = Process.GetCurrentProcess()

               Using curModule As ProcessModule = curProcess.MainModule

                   Return SetWindowsHookEx(WH_MOUSE_LL, proc, GetModuleHandle(curModule.ModuleName), 0)

               End Using

           End Using

       End Function

       Private Delegate Function LowLevelMouseProc(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr

       Private Shared Function HookCallback(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr

           If nCode >= 0 AndAlso MouseMessages.WM_LBUTTONDOWN = CType(wParam, MouseMessages) Then

               Dim hookStruct As MSLLHOOKSTRUCT = CType(Marshal.PtrToStructure(lParam, GetType(MSLLHOOKSTRUCT)), MSLLHOOKSTRUCT)

               Console.WriteLine(hookStruct.pt.x & ", " & hookStruct.pt.y)

           End If

           Return CallNextHookEx(_hookID, nCode, wParam, lParam)

       End Function

       Private Const WH_MOUSE_LL As Integer = 14

       Private Enum MouseMessages

           WM_LBUTTONDOWN = &H201

           WM_LBUTTONUP = &H202

           WM_MOUSEMOVE = &H200

           WM_MOUSEWHEEL = &H20A

           WM_RBUTTONDOWN = &H204

           WM_RBUTTONUP = &H205

       End Enum

       <StructLayout(LayoutKind.Sequential)> _

       Private Structure POINT

           Public x As Integer

           Public y As Integer

       End Structure

       <StructLayout(LayoutKind.Sequential)> _

       Private Structure MSLLHOOKSTRUCT

           Public pt As POINT

           Public mouseData As UInteger

           Public flags As UInteger

           Public time As UInteger

           Public dwExtraInfo As IntPtr

       End Structure

       <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _

       Private Shared Function SetWindowsHookEx(ByVal idHook As Integer, ByVal lpfn As LowLevelMouseProc, ByVal hMod As IntPtr, ByVal dwThreadId As UInteger) As IntPtr

       End Function

       <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _

       Private Shared Function UnhookWindowsHookEx(ByVal hhk As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean

       End Function

       <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _

       Private Shared Function CallNextHookEx(ByVal hhk As IntPtr, ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr

       End Function

       <DllImport("kernel32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _

       Private Shared Function GetModuleHandle(ByVal lpModuleName As String) As IntPtr

       End Function

    End Class

  55. Oliver says:

    Awsome! Thanks publishing this piece!

  56. Geetha says:

    Hi i am using the below code,

    hHookMouse = SetWindowsHookEx((int)HookType.WH_MOUSE_LL,

                       MouseHookProcedure,

                       (System.IntPtr)Marshal.GetHINSTANCE(

                       System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0]),Thread.CurrentThread.ManagedThreadId);

    and my hHookMouse is always returning 0. i want to hook the local module only not the global hook.

  57. dev says:

    hi that works perfectly can you please tell

    suppost i have a app which contains one button can we get that button text when mouse is clicked on that

    button?

    Thanks

  58. firewall says:

    what about ip filter hook?

  59. Nice information. Thank you.

  60. Janes says:

    Excuse me.

    I'm going to try catching the selected text when I right-click them by using global hooks on desktop. Your post helps me learn more about mouse hooks, but it only had an example about mouse location. Could you give me some suggestions about my problem?

  61. Leo King says:

    Hello Stephen,

    I'm trying to run your code in VB.NET and I'm having problems. So I tried running your code in C# and I'm *still* having problems! In C#:

    For this line:

    public static void Main()

           {

               _hookID = SetHook(_proc);

               Application.Run;

               UnhookWindowsHookEx(_hookID);

           }

    The line "Application.Run;" causes an error: "Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement". I've never used C#, is there any way around this? I'd very much love to see a Solution file with this code working.

  62. Mario says:

    Forgive my ignorance but how can I use this class?

  63. Mohammad Syed says:

    Hi Steven,

    I was looking at using the hooking facility to trap mouse messages too in my executable. The documentation of SetWindowsHookEx however has some information about the bitness of the executing assembly. Does it mean that I will have to compile two versions of the application one for x86 and other for x64 and have them co-ordinate regarding the receipt of hook messages? This seems to complicate things a bit. Is there a way to have the hooking implemented with a single version of exe?

    Your comment and help would greatly be appreciated.