Share via


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

  • Anonymous
    July 06, 2006
    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.

  • Anonymous
    July 06, 2006
    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)

  • Anonymous
    July 06, 2006
    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.

  • Anonymous
    August 23, 2006
    The comment has been removed

  • Anonymous
    August 28, 2006
    Yes, it should.

  • Anonymous
    December 29, 2006
    Hello, How can this class be used for controlling remote  System's desktop. Regards,

  • Anonymous
    February 05, 2007
    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.

  • Anonymous
    February 14, 2007
    This code doesnt work with visual c# net2.0 Makes the application bug and crach the CallBack Handler doesn't give anything =(

  • Anonymous
    February 28, 2007
    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.

  • Anonymous
    March 01, 2007
    You can use the System.Diagnostics.Process class to get information about processes on your machine.

  • Anonymous
    March 22, 2007
    Why it it so slow when I click left button in the same application console window?

  • Anonymous
    May 14, 2007
    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.

  • Anonymous
    June 10, 2007
    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?

  • Anonymous
    June 10, 2007
    Sorry, this was supposed to be posted in the keyboard post.

  • Anonymous
    June 14, 2007
    The comment has been removed

  • Anonymous
    July 01, 2007
    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.

  • Anonymous
    July 04, 2007
    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.

  • Anonymous
    July 11, 2007
    great stuff, great work, great example how can i hook all windows messages? all i Know is that i have to use  WH_GETMESSAGE

  • Anonymous
    July 31, 2007
    Hi, How can I get the current module handle in HookCallback? Thank you, Calin

  • Anonymous
    August 13, 2007
    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

  • Anonymous
    August 30, 2007
    The comment has been removed

  • Anonymous
    August 30, 2007
    Hello, if you click left slows down the mouse, application remains to hang!! Did you already see that?

  • Anonymous
    January 02, 2008
    This example and the keyboard hooks example work fine.  Thanks for the info.

  • Anonymous
    January 11, 2008
    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!

  • Anonymous
    March 31, 2008
    Thanks for the code... excellent foundation for my sweet mouse-handling class.

  • Anonymous
    April 03, 2008
    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

  • Anonymous
    April 13, 2008
    The comment has been removed

  • Anonymous
    April 13, 2008
    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

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

  • Anonymous
    June 09, 2008
    how about hooking api of CreateProcess or ExecuteProcess? Thanks, Earl

  • Anonymous
    July 07, 2008
    i need a code to add a new item to the pop-up menu of the computer(right click on the mouse) thanks.

  • Anonymous
    July 31, 2008
    man can you give me a solution to hook keyboard from a windows service.

  • Anonymous
    August 08, 2008
    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...

  • Anonymous
    August 29, 2008
    The comment has been removed

  • Anonymous
    October 30, 2008
    Can this be altered to capture the filename of the process that raised the event?

  • Anonymous
    October 30, 2008
    The comment has been removed

  • Anonymous
    January 03, 2009
    The comment has been removed

  • Anonymous
    March 04, 2009
    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.

  • Anonymous
    April 03, 2009
    how to dispose mouse hook on application close

  • Anonymous
    June 16, 2009
    Please explain How to raise Mouce click hook

  • Anonymous
    June 17, 2009
    PingBack from http://pooltoysite.info/story.php?id=2575

  • Anonymous
    June 29, 2009
    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.

  • Anonymous
    October 04, 2009
    IF you want more help for C# development ... Visit this ... http://mycsharpdotnet.blogspot.com/

  • Anonymous
    October 14, 2009
    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); }

  • Anonymous
    December 16, 2009
    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 :)

  • Anonymous
    December 22, 2009
    Also, it needs: using System.Diagnostics; :)

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

  • Anonymous
    March 10, 2010
    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?!

  • Anonymous
    July 19, 2010
    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

  • Anonymous
    July 19, 2010
    oh, well, "Application" is in System.Windows.Forms

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

  • Anonymous
    January 06, 2011
    It seems that the WH_MOUSE_LL hook doesn't intercept mouse events applied on a ribbon. Is there a solution?

  • Anonymous
    January 08, 2011
    My mistake, it works well, even with ribbon applications.

  • Anonymous
    February 01, 2011
    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

  • Anonymous
    February 14, 2011
    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 :x

  • Anonymous
    May 12, 2011
    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

  • Anonymous
    May 19, 2011
    Awsome! Thanks publishing this piece!

  • Anonymous
    February 21, 2012
    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.

  • Anonymous
    October 05, 2012
    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

  • Anonymous
    October 21, 2012
    what about ip filter hook?

  • Anonymous
    January 03, 2013
    Nice information. Thank you.

  • Anonymous
    May 31, 2013
    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?

  • Anonymous
    November 16, 2013
    The comment has been removed

  • Anonymous
    April 21, 2014
    Thanks!

  • Anonymous
    June 02, 2014
    Forgive my ignorance but how can I use this class?

  • Anonymous
    March 06, 2015
    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.