The keyboard hook from my old blog; “Low Level Keyboard Hook (Global) – Installing a Low Level Keyboard Hook”
This version is slightly updated, to cast the vkCode to the .net Keys enum to make key handling easier.
[16 June 2011 complete rewrite – should now work on every system -_-]
Imports System.Runtime.InteropServices Public Class KeyboardHook <DllImport("User32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)> _ Private Overloads Shared Function SetWindowsHookEx(ByVal idHook As Integer, ByVal HookProc As KBDLLHookProc, ByVal hInstance As IntPtr, ByVal wParam As Integer) As Integer End Function <DllImport("User32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)> _ Private Overloads Shared Function CallNextHookEx(ByVal idHook As Integer, ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer End Function <DllImport("User32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)> _ Private Overloads Shared Function UnhookWindowsHookEx(ByVal idHook As Integer) As Boolean End Function <StructLayout(LayoutKind.Sequential)> _ Private Structure KBDLLHOOKSTRUCT Public vkCode As UInt32 Public scanCode As UInt32 Public flags As KBDLLHOOKSTRUCTFlags Public time As UInt32 Public dwExtraInfo As UIntPtr End Structure <Flags()> _ Private Enum KBDLLHOOKSTRUCTFlags As UInt32 LLKHF_EXTENDED = &H1 LLKHF_INJECTED = &H10 LLKHF_ALTDOWN = &H20 LLKHF_UP = &H80 End Enum Public Shared Event KeyDown(ByVal Key As Keys) Public Shared Event KeyUp(ByVal Key As Keys) Private Const WH_KEYBOARD_LL As Integer = 13 Private Const HC_ACTION As Integer = 0 Private Const WM_KEYDOWN = &H100 Private Const WM_KEYUP = &H101 Private Const WM_SYSKEYDOWN = &H104 Private Const WM_SYSKEYUP = &H105 Private Delegate Function KBDLLHookProc(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer Private KBDLLHookProcDelegate As KBDLLHookProc = New KBDLLHookProc(AddressOf KeyboardProc) Private HHookID As IntPtr = IntPtr.Zero Private Function KeyboardProc(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer If (nCode = HC_ACTION) Then Dim struct As KBDLLHOOKSTRUCT Select Case wParam Case WM_KEYDOWN, WM_SYSKEYDOWN RaiseEvent KeyDown(CType(CType(Marshal.PtrToStructure(lParam, struct.GetType()), KBDLLHOOKSTRUCT).vkCode, Keys)) Case WM_KEYUP, WM_SYSKEYUP RaiseEvent KeyUp(CType(CType(Marshal.PtrToStructure(lParam, struct.GetType()), KBDLLHOOKSTRUCT).vkCode, Keys)) End Select End If Return CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam) End Function Public Sub New() HHookID = SetWindowsHookEx(WH_KEYBOARD_LL, KBDLLHookProcDelegate, System.Runtime.InteropServices.Marshal.GetHINSTANCE(System.Reflection.Assembly.GetExecutingAssembly.GetModules()(0)).ToInt32, 0) If HHookID = IntPtr.Zero Then Throw New Exception("Could not set keyboard hook") End If End Sub Protected Overrides Sub Finalize() If Not HHookID = IntPtr.Zero Then UnhookWindowsHookEx(HHookID) End If MyBase.Finalize() End Sub End Class
Usage:
To create the hook
Private WithEvents kbHook As New KeyboardHook
Then each event can be handled:
Private Sub kbHook_KeyDown(ByVal Key As System.Windows.Forms.Keys) Handles kbHook.KeyDown Debug.WriteLine(Key.ToString) End Sub Private Sub kbHook_KeyUp(ByVal Key As System.Windows.Forms.Keys) Handles kbHook.KeyUp Debug.WriteLine(Key) End Sub
Note: To run this inside Visual Studio, you will need to go to:
Project -> [Project Name] Properties -> Debug -> Uncheck “Enable the Visual Studio hosting process”
As that intercepts the hooked messages before your program.
Hi! As always, a great guide.
But now i have some problems, i can´t see the text it records. How do i do that?
When you handle to events, instead of doing Debug.WriteLine you could, say do Textbox1.text = Key.Tostring or similar
Hi, This is really amazing, thanks a million!
Thanks, it was very usefull!
I’ve succesfuly used this code in a program,I stored the code in a module without a problem.
But now I want to use the same module in a different program and have a problem.
When my progam executes this line:
KeyHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyHookDelegate, System.Runtime.InteropServices.Marshal.GetHINSTANCE(System.Reflection.Assembly.GetExecutingAssembly.GetModules()(0)).ToInt32, 0)
The variable KeyHook remains zero.
And i swear, it’s the same module I use whith succes in an other program.
Does someone has a clue?
Grtz, djenn
I found the problem,
In the project propertys go to the debug page and uncheck:
‘Enable the visual studio host process’
and the job was done
djenn
I tried using your code and it works fine except for tryig to catch special charcters such as the $!@ etc. I have a bar code scanner that i wish to tie into with your code but the Key.Tostring returns D4 for the 4 as well as returning the “$” symbol. Any suggestions?
Check to see if the shift key has been pressed?
Hi,
First of all, I’d like to thank you for this “guide” into keyboard hooking. It’s a quality piece
I encounter the following problem though: In Windows XP, I have no problem at all with the code, it works like a charm. In Windows 7, it doesn’t work at all, and the events do not get raised. It seems as if the keyboard isn’t hooked in the first place to be honest.
Do you know what I should do? I’ll keep an eye out for the answer :-).
Thanks in advance.
Cheers!
Mark
Ive just done a quick search around, and it seems that the hooks should still work on Win7. I just tested some of my old projects that use hooks (Mouse + Keyboard) and both still seem to work fine on Windows 7, so I guess the problem must be on your end?
It works fine but always returns capital key value. even my caps is off it returns capital character
You will need to check the state of the caps lock key then use Char.ToLower(c) (or whatever) based on the state of it.
Have you tryed the solution I game on ?
I had the same problem until I disabled the virtual hosting process on the debug tab
djenn
thank u. It was mos useful.
I want to know how to disable Lwin key & Rwin key tocafe software????
A side question, how do you get that code window in the blog? I am searching for similar thing for my blog.
Its actually just HTML. I wrote a program that takes the text from visual studio and converts it with formatting to html
Would you considering sharing that program?
This is incredibly useful
Just had to improve one tiny thing:
Added the ability to intercept the keys allowing me to make my calculator work without too the numbers phasing through to the application below.
i have problem on this code by using word document.
An outgoing call cannot be made since the application is dispatching an input-synchronous call. (Exception from HRESULT: 0x8001010D (RPC_E_CANTCALLOUT_ININPUTSYNCCALL))”
Source=”Interop.Word”
How would you check the state of the caps lock key? or shift key?
I tried the module, and although it does what it describes, it returns with an error each time:
I am using vb studio 2010.
“A call to PInvoke function ‘iChart!iChart.modHooks+KeyboardHook::CallNextHookEx’ has unbalanced
the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature”
Anyone know how to avoid this?
Thanks,
Funcoder
The aforementioned error occurs at the following line:
Return CallNextHookEx(KeyHook, nCode, wParam, lParam)
Maybe the function definition of
Private Declare Function CallNextHookEx Lib “user32” _
(ByVal hHook As Integer, _
ByVal nCode As Integer, _
ByVal wParam As Integer, _
ByVal lParam As KBDLLHOOKSTRUCT) As Integer
Should be:
_
Private Shared Function CallNextHookEx(ByVal hhk As IntPtr, ByVal nCode As Integer, ByVal wParam As WindowsMessages, ByVal lParam As KBDLLHOOKSTRUCT) As IntPtr
End Function
Or the lParam as ByRef
Thans for sharing!
This was really usefull.
I’ve been getting the same error at the same spot. Windows 7 (VB 2010)
So I replaced the aforementioned troubled function with the recommended:
Private Shared Function CallNextHookEx(ByVal hhk As IntPtr, ByVal nCode As Integer, ByVal wParam As WindowsMessages, ByVal lParam As KBDLLHOOKSTRUCT) As IntPtr
End Function
But the IDE gave the following error:
Error 1 Type ‘WindowsMessages’ is not defined. C:\Users\SVSS\documents\visual studio 2010\Projects\testKeys\testKeys\Cls_KeyboardHook.vb 54 105 testKeys
So I changed the function to the following and the IDE error dissapears:
Private Shared Function CallNextHookEx(ByVal hhk As IntPtr, ByVal nCode As Integer, ByVal wParam As Windows.Forms.Keys, ByVal lParam As KBDLLHOOKSTRUCT) As IntPtr
End Function
So my application seems to work but the IDE ‘Immediate Window’ loggs the following message on every keypress
“A first chance exception of type ‘System.ArgumentOutOfRangeException’ occurred in mscorlib.dll”
Any Ideas?
Oh and I’m not able to find a way to catch the key.modifier to check if shift key is down. Don’t suppose you have an example how the modifier can be tracked. Currently I trap for key up/down events for the key code on the shift key but that seems to take a lot of coding for something so simple.
Hi, I used the class above exactly without any changes. It works perfect In Windows XP, I have no problem at all with the code. In Windows 7, it doesn’t work at all, and the events do not get raised. Thanks in advance.
The problem is probably due to x86/x64 problems with the signatures of the user32 functions. They were all using Integers, however should be using IntPtrs in order to work on both systems. When I tried to run the original code I had posted here on my machine it unbalanced the stack due to incorrect signatures…
Ive completley rewritten the class from scratch and updated it at the top, it should now work!
Thank you very much for your quick reply. But now I have another problem which is HHookID is zero and the message is “Could not set keyboard hook”.
from the form I have:
Private WithEvents kbHook As New KeyboardHook
then in the keyboardHook class
Public Sub New()
HHookID = SetWindowsHookEx(WH_KEYBOARD_LL, KBDLLHookProcDelegate, System.Runtime.InteropServices.Marshal.GetHINSTANCE(System.Reflection.Assembly.GetExecutingAssembly.GetModules()(0)).ToInt32, 0)
If HHookID = IntPtr.Zero Then
Throw New Exception(“Could not set keyboard hook”)
End If
End Sub
Thanks a lot.
Thank you very much for your quick reply. But now I have another problem which is HHookID is zero and the message is “Could not set keyboard hook”.from the form I have:Private WithEvents kbHook As New KeyboardHookthen in the keyboardHook classPublic Sub New()HHookID = SetWindowsHookEx(WH_KEYBOARD_LL, KBDLLHookProcDelegate, System.Runtime.InteropServices.Marshal.GetHINSTANCE(System.Reflection.Assembly.GetExecutingAssembly.GetModules()(0)).ToInt32, 0)If HHookID = IntPtr.Zero ThenThrow New Exception(“Could not set keyboard hook”)End IfEnd SubThanks a lot.
Hi, I replaced your updated code, HHookID is zero. Do you have any suggestion?
Thanks
Hi, I replaced your updated code, HHookID is zero. Do you have any suggestion?Thanks
Finally my program is working perfect, thanks a lot for the help. I have one more question, how can I modify the code above by disable and able some keys, such as alt & tab, alt Esc, Lwin.
If you guys want to check for caps lock or shift state here you go:
If Control.IsKeyLocked(Keys.CapsLock) Or Control.ModifierKeys = Keys.Shift Then
txtKey.Text = Key.ToString.ToUpper
Else
txtKey.Text = Key.ToString.ToLower
End If
I am using your code (successfully) to capture input from my bar code scanner. I am looking for a certain string of characters coming from my bar code reader and then acting on the scan. But… If I know it is from my scanner, i want to do something with the scan and then set the input string to nothing so that if I have another application in focus it does not put my scan in that app.
Example: I have my program in the background waiting for a scan, but a user is also typing a document in Word, someone scans a barcode, my app recognizes it as a scan, deals with it, but then Word puts the text into Word as well. How can I tell Windows to kill the scan message after i receive it so it does not go into word as well?
Thanks in advance!
You would need to return 1 from the KeyboardProc for messages you dont want to be passed on, rather than CallNextHookEx if I remember correctly
sim0n, thanks A LOT for sharing this to us.
I was planing to do a thing and didn’t find how to do that yet.
I’ll explain the situation:
I have a external usb numpad.
I would like to change the entrances from this device to bind another key.
For example, if I press “Numpad7” from the device the OS will receive the word “Test”.
But I want to change just the entrance of my external Numpad, not of my standard keyboard. Do you know what I mean?
On Control Painel -> Keyboard -> Hardware. I can see both keyboards (my standard and the external).
Looks like the solution it’s simple, but I don’t know how to check from wich keyboard the key is typed.
I think the solution would be quite complicated to detect which input the keyboard came from…
Here is an example that I found in C#:
http://www.codeproject.com/KB/system/rawinput.aspx
Thanks again sim0n!
But looks like the solution does not work on Windows 7 64 bits…
I found another posibility uysing a library called SDGT but didn’t work on Win7 64 too…
Well… I’ll keep trying to found a solution and if I succeed, I’ll let you know.
Simon,
Let me start off by saying thank you… Great code!
I am able to get it to work as-is with no problems when the class is located directly within my project.
However, it fails if I use it as an external (VB.Net) dll, or if I simply include the class as part of an external project.
BTW, I am using VisualStudio 2010 on Windows 7 Professional (64-bit).
I have made sure to uncheck the “debug/hosting-process” thing (in the test app as well as the external dll and/or project), but that doesn’t help. No matter what I do, it keeps throwing the code-generated exception when it evaluates HHookID = IntPtr.Zero.
Worse case scenario, I guess I could include it as part of my main project code, but that would cause a host of other issues that I would rather avoid if possible.
Any help would be appreciated… hope you’re still out there and reading this (somewhat old) blog 🙂
Bob
Hi, I am still “out here” reading the comments, however I havn’t looked at most of the code posted here in quite some time!
If you still require help let me know and I’ll try writing an updated class that should hopefully work for you!
I’ve seen a few other comments posted that say there are issues with it on Win7 x64, not sure if it is easily solvable or not but I can investigate if you need!
OUTSTANDING!
Your code “Works-as-advertised”
I’ve been looking for this snippet for 3 weeks!
You’d be amazed how many snippets I’ve tried – and – THEY DON’T WORK!
Thank you SO MUCH for an excellent, working, snippet!
Many Programmers think THEY CAN DO IT ALL. Not True. I’ve written code for over 35 years and I learned a long time ago (and I learned the hard way) that there are two basic types of programmers. “I” am a “Tool-User”. sim0n, you are a “Tool-Maker”.
Basically speaking, Tool-User can build a house . . . because they how to use a hammer. But a Tool-User cannot architect a house, cannot plan out electrical wireing, cannot build a brick fireplace.
A “Tool-User” relies on a “Tool-Maker” for those work pieces.
Thanks sim0n for being a great TOOL-MAKER!
Now I can take your work and build something.
Use
HHookID = SetWindowsHookEx(WH_KEYBOARD_LL, KBDLLHookProcDelegate, 0, 0)
To fix the problem with the IntPtr.Zero Problem.