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 removedAnonymous
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 removedAnonymous
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_GETMESSAGEAnonymous
July 31, 2007
Hi, How can I get the current module handle in HookCallback? Thank you, CalinAnonymous
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 thanksAnonymous
August 30, 2007
The comment has been removedAnonymous
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 asnatAnonymous
April 13, 2008
The comment has been removedAnonymous
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 KhanAnonymous
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, EarlAnonymous
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 removedAnonymous
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 removedAnonymous
January 03, 2009
The comment has been removedAnonymous
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 closeAnonymous
June 16, 2009
Please explain How to raise Mouce click hookAnonymous
June 17, 2009
PingBack from http://pooltoysite.info/story.php?id=2575Anonymous
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 contextAnonymous
July 19, 2010
oh, well, "Application" is in System.Windows.FormsAnonymous
September 14, 2010
@damien just check hookStruct.mouseData (fromHookCallback) value, it differs when you move upward and downward with mouse scroll :pAnonymous
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? ThanksAnonymous
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 :xAnonymous
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 ClassAnonymous
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? ThanksAnonymous
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 removedAnonymous
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.