Visual Basic Hook Routines

SetWindowsHookEx: Detect Caps/Numlock/Scrollock via System-wide Keyboard Hook
     
Posted:   Friday March 28, 2003
Updated:   Monday December 26, 2011
     
Applies to:   VB5, VB6
Developed with:   VB6, Windows XP
OS restrictions:   Windows NT 4.0 SP3 and later
Author:   VBnet, Randy Birch
     

Related:  

SetWindowsHookEx: 'Self-Closing' Message Box using a VB Timer
SetWindowsHookEx: 'Self-Closing' Message Box using SetTimer
SetWindowsHookEx: Detect Caps/Numlock/Scrollock via System-wide Keyboard Hook
SetWindowsHookEx: Customize the API Message Box
SetWindowsHookEx: Trapping Special Key Events using Low Level Hooks
MessageBoxEx: Displaying an API-created Message Box
       
 Prerequisites
Windows NT 4.0 SP3 and later. LowLevelKeyboardProc is unsupported under 9x and ME.

VB5 or VB6 for use of the AddressOf operator.


This routine is pretty straightforward.

Three labels represent the caps, num and scroll keys. On application start-up, GetKeyboardState is called in order to determine the initial state of those keys. If the bdState.kbByte(VK_XXX) = 1, the bit is set and, for the caps, num and scoll keys, indicates those keys are on.

Once the initial state has been processed, a low-level keyboard hook is installed that monitors all keystrokes made.

When the HC_ACTION msg is received, the value from lParam is copied into a KBDLLHOOKSTRUCT type. From that, the kbdllhs.flags member is checked and if the value is LLKHF_UP, the structure's kbdllhs.vkcode is examined. kbdllhs.vkcode indicates the virtual key code for the key pressed, in this case we're interested in VK_NUMLOCK, VK_CAPITAL and VB_SCROLL. Once the virtual key has been determined, GetKeyState is called for that key, and the value returned is compared to &HFF81  (-127).  If the value is -127, the key was pressed on; if -128, the press turned it off. The corresponding label is made visible by converting the value to a boolean through the comparison of the result of GetKeyState and &HFF81. Simple, right? <g> 

As an exe this code will accurately display the active state of the caps, num and scroll keys, regardless of the application that currently has focus.

 BAS Module Code
Hooks require a bas module for the hook code, so add the following to a bas module:

Option Explicit
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Copyright 1996-2011 VBnet/Randy Birch, All Rights Reserved.
' Some pages may also contain other copyrights by the author.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Distribution: You can freely use this code in your own
'               applications, but you may not reproduce 
'               or publish this code on any web site,
'               online service, or distribute as source 
'               on any media without express permission.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public m_hDllKbdHook As Long       'public variable holding
                                   'the handle to the hook procedure
                               
Public Const WH_KEYBOARD_LL As Long = 13 'enables monitoring of keyboard
                                    'input events about to be posted
                                    'in a thread input queue
                                       
Private Const HC_ACTION As Long = 0 'wParam and lParam parameters
                                    'contain information about a
                                    'keyboard message

Public Const VK_CAPITAL As Long = &H14
Public Const VK_NUMLOCK As Long = &H90
Public Const VK_SCROLL As Long = &H91
Private Const LLKHF_UP As Long = &H80&     'test the transition-state flag

Public Type KeyboardBytes
   kbByte(0 To 255) As Byte
End Type

Private Type KBDLLHOOKSTRUCT
  vkCode As Long        'a virtual-key code in the range 1 to 254
  scanCode As Long      'hardware scan code for the key
  flags As Long         'specifies the extended-key flag,
                        'event-injected flag, context code,
                        'and transition-state flag
  time As Long          'time stamp for this message
  dwExtraInfo As Long   'extra info associated with the message
End Type

Public Declare Function SetWindowsHookEx Lib "user32" _
   Alias "SetWindowsHookExA" _
  (ByVal idHook As Long, _
   ByVal lpfn As Long, _
   ByVal hmod As Long, _
   ByVal dwThreadId As Long) As Long
   
Public Declare Function UnhookWindowsHookEx Lib "user32" _
  (ByVal hHook As Long) As Long

Public Declare Function CallNextHookEx Lib "user32" _
  (ByVal hHook As Long, _
   ByVal nCode As Long, _
   ByVal wParam As Long, _
   ByVal lParam As Long) As Long
   
Public Declare Sub CopyMemory Lib "kernel32" _
   Alias "RtlMoveMemory" _
  (pDest As Any, _
   pSource As Any, _
   ByVal cb As Long)

Public Declare Function GetKeyboardState Lib "user32" _
   (kbArray As KeyboardBytes) As Long

Public Declare Function GetKeyState Lib "user32" _
  (ByVal nVirtKey As Long) As Integer


Public Function LowLevelKeyboardProc(ByVal nCode As Long, _
                                     ByVal wParam As Long, _
                                     ByVal lParam As Long) As Long

   Dim kbdllhs As KBDLLHOOKSTRUCT
  
  'Application-defined callback function
  'used with the SetWindowsHookEx function.
  'The system calls this function every
  'time a new keyboard input event is about
  'to be posted into a thread input queue.
  'The keyboard input can come from the local
  'keyboard driver or from calls to the
  'keybd_event function. If the input comes
  'from a call to keybd_event, the input
  'was "injected".
  '
  'If nCode is less than zero, the hook
  'procedure must return the value returned
  'by CallNextHookEx.
  '
  'If nCode is greater than or equal to zero,
  'and the hook procedure did not process the
  'message, it is highly recommended that you
  'call CallNextHookEx and return the value it
  'returns; otherwise, other applications that
  'have installed WH_KEYBOARD_LL hooks will not
  'receive hook notifications and may behave
  'incorrectly as a result.
  '
  'If the hook procedure processed the message,
  'it may return a nonzero value to prevent the
  'system from passing the message to the rest
  'of the hook chain or the target window procedure.
      
  'nCode specifies a code the hook
  'procedure uses to determine how
  'to process the message. HC_ACTION
  'is the only valid code.
   
  'On receipt of the HC_ACTION code,
  'wParam and lParam contain information
  'about a keyboard message, and lParam
  'holds the pointer to a KBDLLHOOKSTRUCT
  'structure.
   If nCode = HC_ACTION Then
   
      Call CopyMemory(kbdllhs, ByVal lParam, Len(kbdllhs))

      If (kbdllhs.flags And LLKHF_UP) Then
      
         Select Case kbdllhs.vkCode
         
            Case VK_NUMLOCK
               Form1.Label1.Visible = (GetKeyState(VK_NUMLOCK) = &HFF81)
               
            Case VK_CAPITAL
               Form1.Label2.Visible = (GetKeyState(VK_CAPITAL) = &HFF81)
            
            Case VK_SCROLL
               Form1.Label3.Visible = (GetKeyState(VK_SCROLL) = &HFF81)
               
            Case Else
         End Select
         
      End If
      
   End If  'nCode = HC_ACTION
  
   LowLevelKeyboardProc = CallNextHookEx(m_hDllKbdHook, _
                                         nCode, _
                                         wParam, _
                                         lParam)
  
End Function
 Form Code
Add three labels (Label1, Label2, Label3) to a form, along with the following code:.

Option Explicit
Private Sub Form_Load()

   Dim kbdState As KeyboardBytes

   Call GetKeyboardState(kbdState)
   
   With Label1
      .Caption = "Numlock is ON"
      .Alignment = vbRightJustify
   End With
   
   With Label2
      .Caption = "Caps lock is ON"
      .Alignment = vbRightJustify
   End With
   
   With Label3
      .Caption = "Scroll lock is ON"
      .Alignment = vbRightJustify
   End With
         
   Label1.Visible = kbdState.kbByte(VK_NUMLOCK) = 1
   Label2.Visible = kbdState.kbByte(VK_CAPITAL) = 1
   Label3.Visible = kbdState.kbByte(VK_SCROLL) = 1

  'set and obtain the handle to the keyboard hook
   m_hDllKbdHook = SetWindowsHookEx(WH_KEYBOARD_LL, _
                                   AddressOf LowLevelKeyboardProc, _
                                   App.hInstance, _
                                   0&)

   If m_hDllKbdHook = 0 Then
   
      MsgBox "Failed to install low-level keyboard hook."
   
   End If
  
End Sub


Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)

   If m_hDllKbdHook <> 0 Then
      Call UnhookWindowsHookEx(m_hDllKbdHook)
   End If
  
End Sub
 Comments

 
 

PayPal Link
Make payments with PayPal - it's fast, free and secure!

 
 
 
 

Copyright 1996-2011 VBnet and Randy Birch. All Rights Reserved.
Terms of Use  |  Your Privacy

 

Hit Counter