Low-level keyboard hook does not detect keystrokes VB.NET

2.2k Views Asked by At

I've been successfully practising low-level keyboard hooks in Visual Basic 2010 (.NET Framework v4.0), then moved the same hook project to Visual Studio 2017 (.NET Framework 4.5.2) and now it does not detect any keystrokes. I've already tried changing the targetted framework to v4.0 but with no result.

There is my code:

'Form1
Private WithEvents KbHook As KeyboardHook

 Private Sub KbHook_KeyDown(Keycode As Keys) Handles KbHook.KeyDown
    MessageBox.Show("Key pressed!")
End Sub

And KeyboardHook class:

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
    Dim ret As Integer = CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam)
    Return ret
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("Error!")
    End If
End Sub

Protected Overrides Sub Finalize()
    If Not HHookID = IntPtr.Zero Then
        UnhookWindowsHookEx(HHookID)
    End If
    MyBase.Finalize()
End Sub
End class

There is a breakpoint set in Form1 on

Private Sub KbHook_KeyDown(Keycode As Keys) Handles KbHook.KeyDown

but the application never breaks while typing on my keyboard. I am pretty confused as it used to work in VB2010... Does anyone have a clue how to solve the problem?

Thanks in advance ;)

1

There are 1 best solutions below

0
On

Thanks for the above code blondkarol and Jim Hewitt. I got it to work in .net 3.5. but it wouldn't work in 4.8 for me. So I incorporated some code from another another post and got the following code to work in .net 4.8.

Imports System.Runtime.InteropServices

Public Class LL_KeyboardHook

   Public Delegate Function CallBack(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer

   <DllImport("user32.dll")>
   Public Shared Function SetWindowsHookEx(idHook As Integer, lpfn As CallBack, hMod As IntPtr, dwThreadId As Integer) As IntPtr
   End Function

   <DllImport("user32.dll")>
   Private Shared Function UnhookWindowsHookEx(idHook As IntPtr) As Integer
   End Function

   <DllImport("user32.dll")>
   Private Shared Function CallNextHookEx(idHook As IntPtr, nCode As Integer, wParam As IntPtr, lParam As IntPtr) As IntPtr
   End Function

   <StructLayout(LayoutKind.Sequential)>
   Private Structure KBDLLHOOKSTRUCT
      Public vkCode As UInteger
      Public scanCode As UInteger
      Public flags As UInteger
      Public time As UInteger
      Public dwExtraInfo As UInteger
   End Structure

   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 HHookID As IntPtr = IntPtr.Zero

   Public Shared Event KeyDown(ByVal Key As Keys)
   Public Shared Event KeyUp(ByVal Key As Keys)

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

      Dim Key As Keys

      If (nCode = HC_ACTION) Then

         Dim struct As KBDLLHOOKSTRUCT

         Select Case wParam
            Case WM_KEYDOWN, WM_SYSKEYDOWN
               Key = CType(CType(Marshal.PtrToStructure(lParam, struct.GetType()), KBDLLHOOKSTRUCT).vkCode, Keys)
               RaiseEvent KeyDown(Key)
               'example below to intercept and not pass along the "A" down key
               'If Key.ToString = "A" Then
               '   Return 1
               '   Exit Function
               'End If

            Case WM_KEYUP, WM_SYSKEYUP
               Key = CType(CType(Marshal.PtrToStructure(lParam, struct.GetType()), KBDLLHOOKSTRUCT).vkCode, Keys)
               RaiseEvent KeyUp(Key)
         End Select
      End If

      Dim ret As Integer = CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam)
      Return ret

   End Function

   Public Sub New()

      Static WindowsHookProc = New CallBack(AddressOf KeyboardProc)
      HHookID = SetWindowsHookEx(WH_KEYBOARD_LL, WindowsHookProc, IntPtr.Zero, 0)

      If HHookID = IntPtr.Zero Then
         Throw New Exception("Error!")
      End If

   End Sub

   Protected Overrides Sub Finalize()
      If Not HHookID = IntPtr.Zero Then
         UnhookWindowsHookEx(HHookID)
      End If
      MyBase.Finalize()
   End Sub

End Class

To use the code in a form:

Private WithEvents kbHook As New LL_KeyboardHook

Private Sub kbHook_KeyDown(Key As Keys) Handles kbHook.KeyDown
   Debug.WriteLine(Key)
End Sub

Private Sub kbHook_KeyUp(Key As Keys) Handles kbHook.KeyUp
   Debug.WriteLine(Key)
End Sub