Visual Basic Hook Routines

SetWindowsHookEx: Trapping Special Key Events using Low Level Hooks
     
Posted:   Tuesday July 30, 2002
Updated:   Monday December 26, 2011
     
Applies to:   VB5, VB6
Developed with:   VB6, Windows XP
OS restrictions:   Windows NT 4.0 SP3 and later
Author:   Mattias Sjögren
     

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
       
 Prerequisites
Windows NT 4.0 SP3 and later. LowLevelKeyboardProc is unsupported under 9x and ME. VB5 or VB6 for use of the AddressOf operator.

When developing applications such as screen savers or kiosk apps it is often a business requirement to restrict the user's ability to interact with the operating system via Windows' defined keyboard combos (ALT+TAB, CTRL+TAB and CTRL+ESC).

By injecting a low level keyboard hook entirely with VB, it is possible to trap these keystroke combinations and to suppress their default actions. Doing so involves simply defining a LowLevelKeyboardProc and passing that to SetWindowsHookEx().  The result is trapping of the keystroke combinations, which can, depending on your application, totally suppress the default action, or invoke an alternate action of their own. This code works across all 32-bit Windows NT-based versions. And when active, the hook processes the keystrokes regardless of the application in the foreground!  Thanks go out to Mattias Sjögren for permission to reproduce his code.

Unlike other demos, this code is set entirely in a bas module and, other than the demo message box, has no user interface. Therefore, this demo's entry point must be set to Sub Main (under project/properties/start up object).

Because of this fact, the code to unhook the lowlevelproc immediately follows the messagebox line. When implementing this code into a final application, the UnhookWindowsHookEx call would be made either in the main form's Unload event, as with normal subclassing. Regardless of where the termination call is placed, like subclassing it is important when moving this code into a real project to ensure care is taken to invoke UnhookWindowsHookEx() on application shutdown in order to restore normal system functionality and to assure project/app ends cleanly.

Note: It is not possible using this code to the trap CTRL+ALT+DEL under either 9x or NT-based operating systems. To do so under NT requires writing and installing a new GINA for the system to handle the security exception, a monumental task only possible for the most adept, and only using C. Under loosey-goosey 9x, where security is a joke and where CTRL+ALT+DEL brings up the Shutdown/Task List, C is still required to write a filter device driver to properly handle this task.

(I would be remiss in not mentioning that a a hack is available on 9x/ME systems that can prevent the use of CTRL+ALT+DEL to bring up the Close Program list, or to shut down Windows, with a side effect of preventing the use of ALT+TAB to switch tasks. This is achieved by tricking the machine into believing that your application is a running screensaver by calling  SystemParametersInfo() with SPI_SCREENSAVERRUNNING. Again, this hack is available on 9x/ME systems only and, as its usefulness is questionable (AKA if you're really interested in security you should be using an NT-based system with appropriate policies enforced), this method is not covered on this site.

For those interested ... the purpose of a GINA (Graphical Identification 'N' Authentication) DLL is to provide customizable user identification and authentication procedures. The default GINA (what you normally invoke via the CTRL+ALT+DEL  key sequence) does this by delegating Secure Attention Sequences (SAS) event monitoring to Winlogon, which receives and processes CTL+ALT+DEL Secure Attention Sequences. A custom GINA is responsible for setting itself up to receive SAS events (other than the default CTRL+ALT+DEL SAS event), and notifying Winlogon when SAS events occur. Winlogon evaluates its state to determine what is required to process the custom GINA's SAS. This processing usually includes calls to the GINA's SAS processing functions.  You can find a lot more info on GINA and its action by querying the MSDN for the search phrase "CTRL+ALT+DEL" and "GINA".

 BAS Module Code
Set your application start up object as Sub Main (under Project / Properties), and place the following code into the general declarations area of 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.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Private Const WH_KEYBOARD_LL = 13&     'enables monitoring of keyboard
                                       'input events about to be posted
                                       'in a thread input queue
                                       
Private Const HC_ACTION = 0&           'wParam and lParam parameters
                                       'contain information about a
                                       'keyboard message

Private Const LLKHF_EXTENDED = &H1&    'test the extended-key flag
Private Const LLKHF_INJECTED = &H10&   'test the event-injected flag
Private Const LLKHF_ALTDOWN = &H20&    'test the context code
Private Const LLKHF_UP = &H80&         'test the transition-state flag

Private Const VK_TAB = &H9             'virtual key constants
Private Const VK_CONTROL = &H11
Private Const VK_ESCAPE = &H1B

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

Private 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
   
Private Declare Function UnhookWindowsHookEx Lib "user32" _
  (ByVal hHook As Long) As Long

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

Private Declare Function GetAsyncKeyState Lib "user32" _
   (ByVal vKey As Long) As Integer

Private m_hDllKbdHook As Long  'private variable holding
                               'the handle to the hook procedure



Public Sub Main()

  '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
  
     'It's hooked! Show a messagebox 
     'to temporarily suspend the app here 
     '(the LowLevelKeyboardProc will continue 
     'to process messages), and follow the 
     'messagebox, for this demo, with the 
     'unhook call. See the text above for 
     'specific information.
      MsgBox "Ctrl+Esc, Alt+Tab and Alt+Esc are blocked. " & _
             "Click OK to quit and re-enable the keys.", _
             vbOKOnly Or vbInformation, _
             "Keyboard Hook Active"
     
     'Placement for this demo only.
     'Move to the unload event of the 
     'main form when used in an application.
      Call UnhookWindowsHookEx(m_hDllKbdHook)
      
   Else
      
      MsgBox "Failed to install low-level keyboard hook - " & Err.LastDllError

  End If
  
End Sub


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

  '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".

   Static kbdllhs As KBDLLHOOKSTRUCT
  
  '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.
    
   
   If nCode = HC_ACTION Then
   
     '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.
      Call CopyMemory(kbdllhs, ByVal lParam, Len(kbdllhs))


     'Ctrl+Esc --------------
      If (kbdllhs.vkCode = VK_ESCAPE) And _
          CBool(GetAsyncKeyState(VK_CONTROL) _
          And &H8000) Then
          
        Debug.Print "Ctrl+Esc blocked"
        
        LowLevelKeyboardProc = 1
        Exit Function
        
      End If  'kbdllhs.vkCode = VK_ESCAPE
          
        
     'Alt+Tab --------------
      If (kbdllhs.vkCode = VK_TAB) And _
          CBool(kbdllhs.flags And _
          LLKHF_ALTDOWN) Then
          
        Debug.Print "Alt+Tab blocked"
        
        LowLevelKeyboardProc = 1
        Exit Function
        
      End If  'kbdllhs.vkCode = VK_TAB
    
    
     'Alt+Esc --------------
      If (kbdllhs.vkCode = VK_ESCAPE) And _
          CBool(kbdllhs.flags And _
          LLKHF_ALTDOWN) Then
          
        Debug.Print "Alt+Esc blocked"
        
        LowLevelKeyboardProc = 1
        Exit Function
      
      End If  'kbdllhs.vkCode = VK_ESCAPE

   End If  'nCode = HC_ACTION
  
  LowLevelKeyboardProc = CallNextHookEx(m_hDllKbdHook, _
                                        nCode, _
                                        wParam, _
                                        lParam)
  
End Function
 Form Code
None.

 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