Introduction
This class allows you to tap keyboard and mouse and/or to detect their activity even when an application runs in the background or does not have any user interface at all. This class raises common .NET events with KeyEventArgs and MouseEventArgs, so you can easily retrieve any information you need.
Background
There are a number of applications that run in the background and detect user inactivity to change their mode. For example, MSN Messenger (or any other messenger). I was going to write such an application, so I searched MSDN and found "exactly" what I needed: 318804 - HOW TO: Set a Windows Hook in Visual C# .NET. This article describes how to tap the mouse movement, but it works only when an application is active. At the end of this article, I found this explanation: "Global hook is not supported in .NET Framework. You cannot implement global hooks in Microsoft .NET Framework...". Anyway, I continued my research and found out that there are exceptions. There are WH_KEYBOARD_LL and WH_MOUSE_LL hooks that can be installed globally. So, I have basically replaced WH_MOUSE with WH_MOUSE_LL in the MSDN example, and it works.
The second step was to extract the information received into a .NET EventArgs and raise the appropriate events.
I found a similar article in CodeProject, under Global System Hooks in .NET by Michael Kennedy, but what I dislike is, there is an unmanaged DLL in C++ that is a main part of this solution. This unmanaged DLL is in C++, and a number of classes make it complicated to integrate it in my own tiny application.
Revisions
This article was posted in 2004 and updated in 2006. During all this time until now I receive a lot of positive feedback and recommendations. There were also a number of technology improvements like .NET Framework 3.5 or Visual Studio 2008. So I have decided to update it once more.
I have refactored and improved the solution, made it more flexible, stable and efficient. But this refactoring also had some drawbacks:
- Number of code lines and files has grown.
- Backward compatibility to older .NETs is lost.
That's why I attend to leave the old version also to be available for download.
Using the Code [Version 2]
The Simple Way
If you are developing a Windows Forms application and prefer drag & drop programming, there is a component named GlobalEventProvider inside the assembly Gma.UserActivityMonitor.dll. Just drag and drop it to your form and create events using the property editor events tab.
The Alternative Way
Use events provided by the static class HookManager. Note that the sender object in events is always null.
For more usage hints, see the source code of the attached demo application.
Using the Code [Version 1]
To use this class in your application, you need to just create an instance of it and hang on events you would like to process. Hooks are automatically installed when the object is created, but you can stop and start listening using appropriate public methods.
UserActivityHook actHook;
void MainFormLoad(object sender, System.EventArgs e)
{
actHook= new UserActivityHook();
actHook.OnMouseActivity+=new MouseEventHandler(MouseMoved);
actHook.KeyDown+=new KeyEventHandler(MyKeyDown);
actHook.KeyPress+=new KeyPressEventHandler(MyKeyPress);
actHook.KeyUp+=new KeyEventHandler(MyKeyUp);
}
Now, an example of how to process an event:
public void MouseMoved(object sender, MouseEventArgs e)
{
labelMousePosition.Text=String.Format("x={0} y={1}", e.X, e.Y);
if (e.Clicks>0) LogWrite("MouseButton - " + e.Button.ToString());
}
Changes and Updates from [Version 0] to [Version 1]
I'd like to thank you all for all the useful comments in the discussion forum. There were a lot of bugs and proposals posted after this article was published on 4th June, 2004. Over and over again came the same topics, and I had to refer to previous posts in the discussion, that is why I have decided to revise the code and publish a FAQ. Here is the list of the most important changes:
- The project was converted into Visual Studio 2005
- The problem with upper case characters is solved
- Mouse wheel information is now included in event arguments
- Better exception handling
- Cancellation of keyboard events using the
Handled property of event arguments
- XML documentation of functions
FAQ [Version 1]
Question
The project cannot be run in Visual Studio .NET 2005 in debug mode because of an exception error caused by calling the SetWindowsHookEx. Why? Is it a problem of .NET 2.0?
Answer
The compiled release version works well so that cannot be a .NET 2.0 problem. To workaround, you just need to uncheck the check box in the project properties that says: "Enable Visual Studio hosting process". In the menu: Project -> Project Properties... -> Debug -> Enable the Visual Studio hosting process.
Question
I need to suppress some keystrokes after I have processed them.
Answer
Just set the e.Handled property to true in the key events you have processed. It prevents the keystrokes being processed by other applications.
Question:
Is it possible to convert your global hooks to application hooks which capture keystrokes and mouse movements only within the application?
Answer
Yes. Just use...
private const int WH_MOUSE = 7;
private const int WH_KEYBOARD = 2;
... everywhere, instead of:
private const int WH_MOUSE_LL = 14;
private const int WH_KEYBOARD_LL = 13;
Question
Does it work on Win98 (Windows ME, Windows 95)?
Answer
Yes and No. The global hooks WH_MOUSE_LL and WH_KEYBOARD_LL can be monitored only under Windows NT/2000/XP. In other cases, you can only use application hooks (WH_MOUSE and WH_KEYBOARD) which capture keystrokes and mouse movement only within the application.
Question
I have a long delay when closing applications using hooks by clicking the x button in the titlebar. If I close the application via another event (button click) for example, that works fine.
Answer
It's a known bug of Microsoft. It has to do with the Windows themes. If you disable the Windows themes, the problem goes away. Another choice is to have the hook code run in a secondary thread.
Question
How do I catch key combinations like Ctrl+Shift+A?
Answer
You'll have to track which keys have gone down but not up. Only the most recently pressed key keeps sending KeyDown messages, but the others will still send a KeyUp when released. So if you make three flags IsCtrlDown, IsShiftDown, and IsADown, and set them to true at KeyDown and false at KeyUp, the expression (IsCtrlDown && IsShiftDown && IsADown) will give you the required result.
Question
Does it works with .NET Framework 1.1 and Visual Studio 2003?
Answer
Yes. The file UserActivityHook.cs can be used without any changes, in a Visual Studio 2003 .NET 1.1 project.
| You must Sign In to use this message board. |
|
|
 |
|
 |
Hi,
If someone double clicks on a text, I am trying to copy this text to clipboard or put in a string variable.
1. How can I tell to double click event what to do? 2. How can I move double clicked text in a variable so I can use this text for the search purpose? 3. How can I transfer mouse selected text to clipboard or a variable.
Thanks.
modified on Monday, November 16, 2009 12:03 AM
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I sent CTRL+C key code. it is copying to clipboard, but it is working for only web pages. I tried on a word document. it didn't copy the text into clipboard. private void HookManager_MouseDoubleClick(object sender, MouseEventArgs e) { textBoxLog.AppendText(string.Format("MouseDoubleClick - {0}\n", e.Button)); SendKeys.Send("^c"); textBoxLog.ScrollToCaret(); }
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
Hi George,
[ edit :
1. strange, the demo version (.exe) of version one will still run in Win Vista SP2 without crashing, but when you close the application it "freezes up" in a strange way for several seconds.
2. opening the source files for version 2, in VS 2010 beta 2, then compiling against FrameWork 3.5 does produce a version (.exe) that works without crashing in Win Vista SP2, but does exhibit the strange "freezing up" for several seconds when you close the app
]
Sorry to trouble you again, but I ran the demo (.exe) of version 2 (from the desktop) today and got this error :
See the end of this message for details on invoking just-in-time (JIT) debugging instead of this dialog box.
************** Exception Text ************** System.IO.FileNotFoundException: Could not load file or assembly 'Gma.UserActivityMonitor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified. File name: 'Gma.UserActivityMonitor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' at Gma.UserActivityMonitorDemo.TestFormStatic.checkBoxOnMouseMove_CheckedChanged(Object sender, EventArgs e) at System.Windows.Forms.CheckBox.OnCheckedChanged(EventArgs e) at System.Windows.Forms.CheckBox.set_CheckState(CheckState value) at System.Windows.Forms.CheckBox.OnClick(EventArgs e) at System.Windows.Forms.CheckBox.OnMouseUp(MouseEventArgs mevent) at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks) at System.Windows.Forms.Control.WndProc(Message& m) at System.Windows.Forms.ButtonBase.WndProc(Message& m) at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m) at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m) at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
WRN: Assembly binding logging is turned OFF. To enable assembly bind failure logging, set the registry value [HKLM\Software\Microsoft\Fusion!EnableLog] (DWORD) to 1. Note: There is some performance penalty associated with assembly bind failure logging. To turn this feature off, remove the registry value [HKLM\Software\Microsoft\Fusion!EnableLog].
"Many : not conversant with mathematical studies, imagine that because it [the Analytical Engine] is to give results in numerical notation, its processes must consequently be arithmetical, numerical, rather than algebraical and analytical. This is an error. The engine can arrange and combine numerical quantities as if they were letters or any other general symbols; and it fact it might bring out its results in algebraical notation, were provisions made accordingly." Ada, Countess Lovelace, 1844
modified on Friday, November 6, 2009 10:47 PM
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
Hello,
First thank you for posting this sample. It's exactly what I've been looking for. The question though as I go through this is where though do I perform the check for the key that is pressed to base a action off. For example when the 'A' key is pressed down, I may want to throw a message. Do I put that in the HookHanlder.Callbacks class? Again, thanks for any assistance and for putting this sample out here.
Michael
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
This is my code added to a basic Windows Form:
private void Form1_Load(object sender, EventArgs e) { this.hookManager = new UserActivityHook(false, true);
this.hookManager.KeyDown += new KeyEventHandler(hookManager_KeyDown); }
private void hookManager_KeyDown(object sender, KeyEventArgs e) { StringBuilder keys = new StringBuilder();
if (e.Control) { keys.Append("Ctrl + "); }
if (e.Alt) { keys.Append("Alt + "); }
keys.Append(e.KeyCode.ToString());
Console.WriteLine(keys.ToString()); }
I keep getting a Win32Exception on line 538, error code 1400. I'm using version 1 source code. I tried the version 2 demo but I get a runtime error when I put the checkmark in KeyPress. Version 1 demom works.
|
| Sign In·View Thread·PermaLink | 2.00/5 |
|
|
|
 |
|
 |
Hi George,
Once again, many thanks for this "classic gem" of a program which I've been using for years ! And for "sticking around" to support it for so long.
It is interesting to me that I was using a version of GlobalHook compiled against FrameWork 4.0 in VS Studio 2010 beta 1 with no problems 
And, I find that if I compile GlobalHook against FrameWork 3.5 in VS Studio 2010 beta 2, there is no problem.
So really there's no problem here for me. I'm not complaining 
Here's a short description of the error thrown in Visual Studio 2010 beta 2 compiled against FrameWork 4.0 :
throw new Win32Exception(errorCode);
Error code #87 (-2147467259) is being passed to Win32Exception, and it is failing with a "parameter incorrect" error there.
Wish I knew enough about API programming to research this one on my own, but I try and stay clear of it.
Again, thanks !
best, Bill
"Many : not conversant with mathematical studies, imagine that because it [the Analytical Engine] is to give results in numerical notation, its processes must consequently be arithmetical, numerical, rather than algebraical and analytical. This is an error. The engine can arrange and combine numerical quantities as if they were letters or any other general symbols; and it fact it might bring out its results in algebraical notation, were provisions made accordingly." Ada, Countess Lovelace, 1844
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
See FAQ 1 at the end of the article. Let me know if it worked. Several people were already able to port it to VS 2010
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi George,
George Mamaladze wrote: Several people were already able to port it to VS 2010
In fact, as I noted, I was using GlobalHook compiled against FrameWork 4.0 with no problems in Visual Studio 2010 beta 1.
George Mamaladze wrote: See FAQ 1 at the end of the article.
I did ready your FAQ, carefully. I even made sure to "uncheck the check box in the project properties that says: "Enable Visual Studio hosting process".
And I tested this in both Debug and Release compile modes from within VS Studio 2010 beta 2, and, with VS Studio shut down, by running the '.exes for your demo project (both debug and release versions).
My guess is this is some internal change in Win32Exception error codes, but this is beyond my abilities to debug and follow-up on.
Again, thanks for continuing to maintain this excellent tool !
best, Bill
"Many : not conversant with mathematical studies, imagine that because it [the Analytical Engine] is to give results in numerical notation, its processes must consequently be arithmetical, numerical, rather than algebraical and analytical. This is an error. The engine can arrange and combine numerical quantities as if they were letters or any other general symbols; and it fact it might bring out its results in algebraical notation, were provisions made accordingly." Ada, Countess Lovelace, 1844
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I got the same problem now when installing beta Would really appreciate a fix for this as there is no alternatives AFAIK
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi Mattiask,
I'm almost "embarassed" to ask George for help with this since he's supported this most useful article/technique ... so generously ... for so long.
Assuming you are speaking of your experience in Visual Studio 2010 beta 2 (are you ?) :
Remember : I had no problem with this in VS Studio 2010 beta 1), using the GlobalHook compiled against FrameWork 4.0.
You can use the work-around of compiling the GlobalHook against FrameWork 3.5 in VS Studio 2010 beta 2, and then using that dll in a VS 2010 beta two project compiled against FrameWork 4.
So there is a solution. And the fact that compiling GlobalHook against VS Studio Beta 2 and FrameWork 4.0 results in the same immediate error the moment you try and set the hook indicates to me the solution can be, possibly, as simple as finding out why this particular error code (#87) is no longer "edible" by Win32Exception ... that's easy for me to say, because I am not very competent in WinAPI programming The reality may be much more difficult than I suspect.
best, Bill
"Many : not conversant with mathematical studies, imagine that because it [the Analytical Engine] is to give results in numerical notation, its processes must consequently be arithmetical, numerical, rather than algebraical and analytical. This is an error. The engine can arrange and combine numerical quantities as if they were letters or any other general symbols; and it fact it might bring out its results in algebraical notation, were provisions made accordingly." Ada, Countess Lovelace, 1844
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi,
Yah I was thinking of the same solution, except it doesnt work for me I set the target framework for the hook assembly to 3.5, compile it and set my project to the file reference rather than project reference, but I'm still getting the error. I've doublechecked and the run-time version for the asssembly is indeed 2.0 (v2.0.50727) What am I missing? (probably something stupid :P)
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
I'm sorry but i cannot find the needed *.dll in the available download links to debug the source coud. Can anybody help me?
Regards
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
Thanks a lot. The problem was that i get a warning "the project location is not trusted". So i couldnt compile the code. Problem is now solved and i get the *.dll!
Nice weekend
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
The problem was my working station! After that i posted here i changed a few settings and so the compiling was successfully. Thanks a lot.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
The faq contains an answer to
"I have a long delay when closing applications using hooks by clicking the x button in the titlebar. If I close the application via another event (button click) for example, that works fine."
stating that this is a known Microsoft bug.
Any chance, you (or someone else) got a link to where I might read more about this bug? I have tried googling it, but without any luck so far.
Thanks, Joan
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hello, I too am interested if someone finds any info or solution to the Microsoft Theme’d bug.
In the meantime, I've been digging into this, and unfortunately I don’t have any sites that discuss this Windows Theme bug, however, I did notice that the delay is also exists when minimizing or maximizing the form. Which led me to my own work-a-round…that being: I simply subclassed the form, set the FormBorderStyle = None, and painted my own Title Bar with owner drawn Min, Max, and Close buttons. Not the best work-a-round, but at least no delay.
On the positive side, I have full control of the forms appearance (via Paint), so I’ve rounded the corners and (I guess) made the form look similar to a WPF app.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
George Thanks for the code - it saved me writing my own. I've updated the component to add booleans to the properties so that the user can select at design time whether they want to hook globally or just in the application - it defaults to globally, and I've added c# properties so that you get design time prompts (English only) for the various event types. Happy to share these updates if they are of interest to you. Andy
|
| Sign In·View Thread·PermaLink | 5.00/5 |
|
|
|
 |
|
 |
Demo application[version 2] IS blocked by Trend AntiVirus software.
Is there any way to work around except "close the antivirus software"?
Thanks.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
Hi George,
Thanks so much for "sticking with" this and supporting it for so long !
best, Bill
"Many : not conversant with mathematical studies, imagine that because it [the Analytical Engine] is to give results in numerical notation, its processes must consequently be arithmetical, numerical, rather than algebraical and analytical. This is an error. The engine can arrange and combine numerical quantities as if they were letters or any other general symbols; and it fact it might bring out its results in algebraical notation, were provisions made accordingly." Ada, Countess Lovelace, 1844
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|