I have seen a lot of people asking about hooking
WM_CHAR messages. There are a lot of articles on keyboard hooking using
WH_KEYBOARD_LL. But these hooks trap the
WM_SYSKEYDOWN messages. These messages send virtual key codes and scan codes. Anyone who wants to work with ASCII values has to convert these virtual key codes to ASCII values. However, not all ASCII values have corresponding virtual key codes defined. Moreover, there are no virtual key codes for extended ASCII values (codes greater than 127) and punctuation marks like ,. etc. So, how can a user output an extended character when key 'A' is pressed ? The solution is hooking
About the WH_GETMESSAGE hook
When a key is pressed, Windows sends a
WM_KEYDOWN message followed by a
WM_CHAR message. Not all
WM_KEYDOWN messages are followed by a
WM_CHAR message. There are other messages like
WM_SYSDEADCHAR, which Windows sends to applications. Anyway, I am not going to discuss about these messages. Now, coming to our subject, we can trap a
WM_CHAR message using the
WH_GETMESSAGE hook. The
WH_GETMESSAGE hook enables an application to monitor messages about to be returned by the
PeekMessage() function. You can use the
WH_GETMESSAGE hook to monitor mouse and keyboard input and other messages posted to the message queue. In the hook procedure, the message parameters can be changed, new messages can be sent, or the message can be blocked from reaching the application.
Hooks can be installed using the
SetWindowsHookEx function and removed using the
UnhookWindowsHooks function. The syntax for these functions are:
HHOOK SetWindowsHookEx( int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId);
idHook - [in] - Specifies the type of hook procedure to be installed. We will be using
WH_GETMESSAGE here. For other values, refer to MSDN.
lpfn - [in] - Pointer to the hook procedure. If the
dwThreadId parameter is zero or specifies the identifier of a thread created by a different process, the
lpfn parameter must point to a hook procedure in a DLL. Otherwise,
lpfn can point to a hook procedure in the code associated with the current process.
hMod - [in] - Handle to the DLL containing the hook procedure pointed to by the
lpfn parameter. The
hMod parameter must be set to
NULL if the
dwThreadId parameter specifies a thread created by the current process and if the hook procedure is within the code associated with the current process.
dwThreadId - [in] - Specifies the identifier of the thread with which the hook procedure is to be associated. If this parameter is zero, the hook procedure is associated with all existing threads running in the same desktop as the calling thread.
If the function succeeds, the return value is the handle to the hook procedure. If the function fails, the return value is
BOOL UnhookWindowsHookEx( HHOOK hhk);
hhk - [in] - Handle to the hook to be removed. This parameter is a hook handle obtained by a previous call to
Using the code
Here, we will be developing a system wide hook for
WH_GETMESSAGE. As most of you might know, a system wide hook must be implemented using a DLL. We will be developing a DLL which contains the hook procedure, and an application to install or remove the hook.
Create a Win32 DLL project and select 'A simple DLL project option' in step 1. Visual Studio will create the necessary files. Open the source file in which the function
DllMain is defined. This is the function which is called by the system whenever this DLL is attached/detached to a process/thread. Add two global variables at the top of the code file, as shown below.
HHOOK hkKey = NULL;
#pragma data_seg() //end of our data segment
Here, we have created two global variables. The variable
hInstHookDll will store the
hinstance of our DLL. The
hinstance will be passed to us as a parameter to the
DllMain function. The variable
hkKey will be returned by calling
SetWindowsHooksEx(). The hook procedure, which we will develop, and the
UnhookWindowsEx() function will require this variable. Notice that this variable is stored in a new data segment,
Shared. This is done because this variable will be shared between all the processes to which our DLL code is loaded.
We need to initialize our global variables. As said earlier, our DLL instance is passed as an argument to the
DllMain() function. We will be initializing
hInstHookDll when the DLL is attached to a process.
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call,
LPVOID lpReserved )
hInstHookDll = (HINSTANCE)hModule;
We will be creating two functions,
RemoveHook(), in which we will write code to set and remove our global hook.
void __stdcall SetHook()
if(hkKey == NULL)
hkKey = SetWindowsHookEx(WH_GETMESSAGE,procCharMsg,hInstHookDll,0);
void __stdcall RemoveHook()
hkKey = NULL;
procCharMsg is our hook procedure that will be called by the system before the message is retrieved by the application using the
PeekMessage functions. The code for the hook procedure goes like this:
LRESULT CALLBACK procCharMsg(int nCode,WPARAM wParam, LPARAM lParam)
if(nCode >=0 && nCode == HC_ACTION)
charCode = msg->wParam;
nCode is greater than or equal to 0 and is
HC_ACTION, we will process the message. The
lParam parameter is a pointer to the MSG structure. We will check the MSG structure's
message data member. If it is a
WM_CHAR message, we will process this message. The MSG structure's
wParam is the character code. We will check if the character is lower case; if so, we will change it to upper case. We will pass this message to the next hook using the
Lastly, we create a definition file (.def) to export our functions:
Finally, build the DLL.
Create a Win32 application and add two menus: 'Set Hook' and 'Remove Hook'. Make changes to the application's window procedure.
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
case IDC_START_HOOK: hmodDll = LoadLibrary("hookDll.dll");
if(hmodDll == NULL)
ptrFunc = (pFunc)GetProcAddress(hmodDll,"SetHook");
if(ptrFunc == NULL)
if(hmodDll == NULL)
ptrFunc = (pFunc)GetProcAddress(hmodDll,"RemoveHook");
if(ptrFunc == NULL)
ptrFunc(); FreeLibrary(hmodDll); ptrFunc = NULL;
hmodDll = NULL;
DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
return DefWindowProc(hWnd, message, wParam, lParam);
In the command handlers, we load our library and get the function address of
RemoveHook and store it into a function pointer. Then, we will call the function using the function pointer. We have to define the function pointer type and a variable.
Define the function pointer type at the top of the code file:
typedef void (__stdcall *pFunc)(void);
Define the static variables in the window procedure to store the value returned by the
LoadLibrary() function and the pointer function variable.
static HMODULE hmodDll = NULL;
static pFunc ptrFunc;
Build the application and copy the DLL that was created above to the application's executable folder. Run the application and click the 'Set Hook' menu. Run the Notepad application and type something. You can see that all the characters typed will be in uppercase. Now, click 'Remove Hook' menu in our application. The uppercase behaviour in the Notepad application will be removed.