1. Introduction to Keyboard Hooks and Filter Functions
A Windows Hook (Keyboard Hook is a Subtype) intercepts specific type of windows hardware Event before it hits its last target say an SDI window. MFC messaging will pass this intercepted hardware events to a special function, which can change the event and even can discard it. We call such a function as a Filter function. There are unique types of windows hook available.
1.1 Hook Messages & Filters
Below are the most used Hook Messages, which are taken from the MSDN (Microsoft Developer Network):
WH_KEYBOARD:
The WH_KEYBOARD is the Keyboard Hook Message. Windows will deliver this intercepted message to the keyboard filter function.
WH_MOUSE:
The WH_MOUSE is the Hook Message for the Mouse Events. Windows will deliver this message to the mouse filter function.
WH_GETMESSAGE
This Hook Message is to intercept all the messages posted to the windows message queue. By defining the filter function, we can intercept any windows message and discard them.
We should attach a hook to a Filter Function. For Example, when we attach the WH_MOUSE hook to a filter function, all the mouse hardware messages first go to the filter function before it reaches the target window. In this article, we are going to use the Keyboard hook. Note, we can also call the filter function as hook procedure as we attach it to a hook.
1.2 Hook Chain
We can attach more than one filter functions to a windows hook. This forms a Hook Chain. For example, let us say you have two filter functions for a WH_KEYBOARD, then this forms a hook chain. When a keyboard event triggers, it reaches to all the filter functions in the keyboard hook chain. Have a look at the below picture:

Hope you went through the above picture. Let us say user pressed a numeric key ‘7’ to show that in the notepad. The keyboard hook watches the hardware event of pressing the keyboard key. So, before this hardware event ends into the notepad, the keyboard hook ( WH_KEYBOARD) will direct that to the tied filter functions. The filter function(s) after dealing with the message sends it to the actual target window or application.
From the above picture, you can see four filter functions attached to the keyboard hook. Let us say we added the filter function D at last. Now, the lastly added filter function receives the tapped keyboard event first and after processing, it sends that to Filter function ‘C’. This way the event reaches the notepad application at last once all the filter function in the chain processed the message. Ok, let us go ahead & create the example application.
2. About Keyboard Hook Example
The example is an MFC SDI application. Refer the picture below:

The SDI sample, under the file menu, has two menu items called Hook CAPS Lock, Unhook CAPS Lock. When the user clicks the Hook Caps Lock menu item, the Keyboard hook is attached to the application and hooks the keyboard event. The UnHook CAPS Lock event removes installed hook from the application.
Our example SDI contains two menu items called First Dialog and Second Dialog under view menu. These menu items will bring up two dialogs with text boxes in it. When a hook is installed, you can type only the capital letters in the dialogs. After opting for the Un-Hook action, you will not have upper case constraint on the dialogs. Let us jump into the explanation-based walk-through.
3. Create Menus & Dialogs of Example
3.1 Creating the Example
First, we create a MFC Single document Interface Project called WinHooks. While creating the project, we remove the support for Document/View architecture and Unicode libraries. For this example, we can accept the defaults for all other settings. This is shown in the below video:
Video 1: Creating the Example
3.2 Adding MFC Dialog Resources To Test Keyboard Hooks
Once the project is ready, we add two dialog templates to it as shown below:

In each dialog, we add some text boxes. After the dialog design, we also add the dialog class using the class wizard, which can be accessed through the context menu of the dialog template. Adding the dialog through resource and attaching a class to it is shown in the below video:
Video 2: Adding Dialog Templates
3.3 Adding Menus
After adding the dialog templates to the project via the dialog editor, we need to add two menu items and handler functions. From these handler functions, we will launch our dialogs. Below video shows adding two menu items:
Video 3: Adding Menu Items
Like how we added the menu items for launching the dialogs, the same way we add two menu items under the file menu to engage and disengage the hook for spying the keyboard events. The picture in section 2 shows the display names of these menu items.
4. Display Dialogs
In the MainFrm.cpp file, we provide the #include
pre-processor directive to include the dialog classes. Below are the two statements:
1 2 3 |
//Sample 01: Header files to access the dialogs #include "Dialog1.h" #include "Dialog2.h" |
Next, in the menu items handler (Created in video 3), we make call to the DoModal
API which brings the dialog in front of the user. Note, we are launching the dialogs as Modal dialogs. Below is the code that displays the dialogs:
1 2 3 4 5 6 7 8 9 10 11 12 |
void CMainFrame::OnViewFirstdlg() { //Sample 02: Open the First Dialog CDialog1 dlg; dlg.DoModal(); } void CMainFrame::OnViewSeconddlg() { //Sample 03: Open Second Dialog CDialog2 dlg; dlg.DoModal(); } |
5. Add Hook Filter Functions
Hope by this time you are aware of what is a filter function. In this section, we will create a filter function and it will be attached to the Keyboard Hook which we will create in the next section.
5.1 Declare Hook Handle
First, we add the Win32 handle of type HHOOK as a global variable. Note, we are going to add a global filter function sooner and for convenience we are adding the win32 handle at a global level. This is to hold the Windows Hook in a Windows Handle. Below is the Declaration:
1 2 |
//Sample 04: Win32 Handle to Keyboard Hook HHOOK hKBHook; |
5.2 Keyboard Hook Procedure Signature
The signature of the keyboard hook procedure is given below. In the keyboard hook procedure, the code shows how should a procedure respond. That is; when we receive the negative code
in the first param; the keyboard procedure will call the next hook filter function in the hook chain. In our example, we are going to test the WPARAM
to know what key is pressed. The LAPARAM
is useful to know repeat count of a button; say, for Example, a keyboard button Up Arrow pressed down for 4 or 5 seconds.
1 2 3 |
//Sample 05: A Global Filter function that will be hooked to the // Key board event LRESULT CALLBACK KeyboardProc ( int code, WPARAM w, LPARAM l ) |
5.3 Traversing the Filter Chain
As already told, we examine the first parameter to see if the filter function can process the message. When the code is lesser than zero, we should do nothing but make a call to the CallNextHookEx. The API will take care of calling the next filter function in the hook chain. Below is code:
1 2 3 4 |
//Sample 5.1: As Per MSDN, Do not intercept the message when //code is negative if ( code < 0 ) return CallNextHookEx ( hKBHook, code, w, l ) ; |
5.4 CAPS LOCK Status Filter
Next using the W PARAM w, we test to see the CAPS Lock key is pressed. Have a look at the below depiction:

Note that each slot in the above picture denotes the unsigned char and hence each slot consumes 8 bits. The constant VK_CAPITAL
denotes 20 and using the GetKeyBoardState
function call we get all the key status in an array of 256 unsigned char slot. At the char location 20, we get the CAPS lock key status. When lower order bit is 1 then the CAPS Lock status is ON. So, our filter function gets the keyboard status and always keeps the lower order bit of VK_CAPITAL
as one as shown above. The net effect is, when the message reaches the application message queue, the CAPS is always ON. Below is the code which is taken from the filter function:
1 2 3 4 5 6 7 8 |
//Samle 5.2: Always keep the CAPS lock state to 1. unsigned char state [256] ; if ( w == VK_CAPITAL ) { GetKeyboardState(state); state [VK_CAPITAL] = 1 ; SetKeyboardState ( state ) ; } |
5.5 Don’t Break Filter Chain
Once we are done dealing with the CAPS LOCK Toggle state, make a call to the next filter function as shown below:
1 2 |
//Sample 5.3: Call the Next Hook in Chain return CallNextHookEx ( hKBHook, code, w,1 ) ; |
The complete Filter Function For Keyboard Hook is below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
//Sample 05: A Global Filter function that will be hooked to the //Key board event LRESULT CALLBACK KeyboardProc ( int code, WPARAM w, LPARAM l ) { //Sample 5.1: As Per MSDN, Do not intercept the message when // code is negative if ( code < 0 ) return CallNextHookEx ( hKBHook, code, w, l ) ; //Samle 5.2: Always keep the CAPS lock state to 1. unsigned char state [256] ; if ( w == VK_CAPITAL ) { GetKeyboardState(state); state [VK_CAPITAL] = 1 ; SetKeyboardState ( state ) ; } //Sample 5.3: Call the Next Hook in Chain return CallNextHookEx ( hKBHook, code, w,1 ) ; } |
6. Link Keyboard Hook & Filter Function
Recall, the filter function is ready in the past section and now it is time to link the hook and with its filter function.
6.1 Set Initial CAPS Lock State
Before that we will set the Initial CAPS Lock state as ON. Below is the code called from the application’s InitInstance
that an MFC programmer is aware of:
1 2 3 4 5 6 |
//Sample 06: Set the Keyboard state unsigned char state [256] ; GetKeyboardState(state); if (state[VK_CAPITAL] == 0 ) state [VK_CAPITAL] = 1 ; SetKeyboardState ( state ) ; |
6.2 Using SetWindowsHookEx API
The SetWindowsHookEx Win32 function installs the windows hook and attaches that to the specific filter function routine. Have a look at the below picture:

The first argument passed to this function is to mention the hook category. In our example, we are will hook the keyboard events and hence the first param passed to this function is WH_KEYBOARD. The second param passed to this function is a callback to the filter function, which will get all the intercepted keyboard messages. The third param denoted the module handle. Say, for example, if the hook filter function is in the DLL module, this parameter shows that DLL module name. In our case, this argument is null, as same EXE module is defining the filter function. The final param species the thread handle linked to the hook procedure. So we make a call to the GetCurrentThreadId to state the Filter Function is also running in the same thread.
6.3 Uninstalling the Hook
While SetWindowsHookEx installs the Keyboard Hook the API UnhookWindowsHookEx uninstalls the Hook. Note how we use the handle which we retrieved via SetWindowsHookEx API call. Below is the message map handler functions for the menu items in those handlers we will hook and unhook keyboard messages:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
void CMainFrame::OnFileSetcapslockhook() { //Sample 07: Set the Key Board Hook hKBHook = SetWindowsHookEx( WH_KEYBOARD, KeyboardProc, NULL, GetCurrentThreadId()); } void CMainFrame::OnFileUnhookcapslock() { //Sample 08: Unset the Key Board Hook UnhookWindowsHookEx(hKBHook); } |
7. Running Keyboard Hook Example
After setting the hook, launch the dialog. Once the dialogs are launched, you can see the dialog accepts only the Capital letters. You can see this in the below video:
Video 4: Running the Application
Source Code: Download
Categories: MFC
Tags: CallNextHookEx, HHOOK, SetWindowsHookEx, UnhookWindowsHookEx, WH_KEYBOARD