|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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 |
|
|
|
|||||
|
|||||
|
|||||
Copyright ©1996-2011 VBnet and Randy Birch. All Rights Reserved. |