Let us now write a program that keeps the CapsLock permanently on. This effect would come into being when the first key is hit subsequent to the execution of our program. In fact there would be two programs:
(a) A DLL containing a hook procedure that achieves the CapsLock effect.
(b) An application EXE which loads the DLL in memory
.
Given below is the source code of the DLL program
Given below is the source code of the DLL program
/* hook.c */
# include <windows.h>
static HHOOK hkb = NULL ;
HANDLE h ;
BOOL __stdcall DllMain ( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )
{
h = hModule ;
return TRUE ;
}
BOOL __declspec ( dllexport ) installhook( )
{
hkb = SetWindowsHookEx ( WH_KEYBOARD,
( HOOKPROC ) KeyboardProc, ( HINSTANCE ) h, 0 ) ;
if ( hkb == NULL )
return FALSE ;
return TRUE ;
}
LRESULT __declspec ( dllexport ) __stdcall KeyboardProc ( int nCode, WPARAM wParam, LPARAM lParam )
{
short int state ;
if ( nCode < 0 )
return CallNextHookEx ( hkb, nCode, wParam, lParam ) ;
if ( ( nCode == HC_ACTION ) &&
( ( DWORD ) lParam & 0x40000000 ) )
{
state = GetKeyState ( VK_CAPITAL ) ;
if ( (state & 1 )== 0) /* if off */
# include <windows.h>
static HHOOK hkb = NULL ;
HANDLE h ;
BOOL __stdcall DllMain ( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )
{
h = hModule ;
return TRUE ;
}
BOOL __declspec ( dllexport ) installhook( )
{
hkb = SetWindowsHookEx ( WH_KEYBOARD,
( HOOKPROC ) KeyboardProc, ( HINSTANCE ) h, 0 ) ;
if ( hkb == NULL )
return FALSE ;
return TRUE ;
}
LRESULT __declspec ( dllexport ) __stdcall KeyboardProc ( int nCode, WPARAM wParam, LPARAM lParam )
{
short int state ;
if ( nCode < 0 )
return CallNextHookEx ( hkb, nCode, wParam, lParam ) ;
if ( ( nCode == HC_ACTION ) &&
( ( DWORD ) lParam & 0x40000000 ) )
{
state = GetKeyState ( VK_CAPITAL ) ;
if ( (state & 1 )== 0) /* if off */
{
keybd_event ( VK_CAPITAL , 0, KEYEVENTF_EXTENDEDKEY, 0 ) ;
keybd_event ( VK_CAPITAL , 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0 ) ;
}
}
return CallNextHookEx ( hkb, nCode, wParam, lParam ) ;
}
BOOL __declspec ( dllexport ) removehook( )
{
return UnhookWindowsHookEx ( hkb ) ;
}
keybd_event ( VK_CAPITAL , 0, KEYEVENTF_EXTENDEDKEY, 0 ) ;
keybd_event ( VK_CAPITAL , 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0 ) ;
}
}
return CallNextHookEx ( hkb, nCode, wParam, lParam ) ;
}
BOOL __declspec ( dllexport ) removehook( )
{
return UnhookWindowsHookEx ( hkb ) ;
}
Follow the steps mentioned below to create this program:
(a) Select ‘File | New’ option to start a new project in VC++.
(b) From the ‘Project’ tab select ‘Win32 Dynamic-Link Library’ and click on the ‘Next’ button.
(c) In the ‘Win32 Dynamic-link Library Step 1 of 1’ select “An empty DLL project” and click on the ‘Finish’ button.
(d) Select ‘File | New’ option.
(e) From the ‘File’ tab select ‘C++ source file’ and give the file name as ‘hook.c’. Type the code listed above in this file.
(f) Compile the program to generate the .DLL file.
(b) From the ‘Project’ tab select ‘Win32 Dynamic-Link Library’ and click on the ‘Next’ button.
(c) In the ‘Win32 Dynamic-link Library Step 1 of 1’ select “An empty DLL project” and click on the ‘Finish’ button.
(d) Select ‘File | New’ option.
(e) From the ‘File’ tab select ‘C++ source file’ and give the file name as ‘hook.c’. Type the code listed above in this file.
(f) Compile the program to generate the .DLL file.
Note that this program doesn’t contain WinMain( ) since the program on compilation should not execute on its own. It has been replaced by a function called DllMain( ). This function acts as entry point of the DLL program. It gets called when the DLL is loaded or unloaded.
When the application loads the DLL the DllMain( ) function would be called. In this function we have merely stored the handle to the DLL that has been loaded in memory into a global variable h for later use.
Those functions in a DLL that can be called from outside it are called exported functions. Our DLL contains three such functions—installhook( ), removehook( ) and KeyboardProc( ). To indicate to the compiler that a function in a DLL is an exported function we have to pre-qualify it with __declspec ( dllexport ). These functions would be called from the second program. This second program is a normal GUI application created in the same way that we did applications in Chapters 17 and 18. The handlers for messages WM_CREATE and WM_DESTROY are given below:
/* capslocked.c */
HINSTANCE h ;
void OnCreate ( HWND hWnd )
{
BOOL ( CALLBACK *p )( ) ;
h = LoadLibrary ( "hook.dll" ) ;
if ( h != NULL )
{
p = GetProcAddress ( h, "installhook" ) ;
( *p )( ) ; /* calls installhoook( ) function */
}
}
void OnDestroy ( HWND hWnd )
{
BOOL ( CALLBACK *p )( ) ;
p = GetProcAddress ( h, "removehook" ) ;
( *p )( ) ; /* calls removehoook( ) function */
FreeLibrary ( h ) ;
PostQuitMessage ( 0 ) ;
}
As we know, the OnCreate( ) and OnDestroy( ) handlers would be called when the WM_CREATE and WM_DESTROY messages arrive respectively. In OnCreate( ) we have loaded the DLL containing the hook procedure. To do this we have called the LoadLibrary( ) API function. Once the DLL is loaded we have obtained the address of the exported function installhook( ) using the GetProcAddress( ) API function. The returned address is stored in p, where p is a pointer to the installhook( ) function. Using this pointer we have then called the installhook( ) function.
In the installhook( ) function we have called the API function SetWindowsHookEx( ) to register our hook procedure with the OS as shown below:
hkb = SetWindowsHookEx ( WH_KEYBOARD,
( HOOKPROC ) KeyboardProc, ( HINSTANCE ) h, 0 ) ;
Here the first parameter is the type of hook that we wish to register, whereas the second parameter is the address of our hook procedure KeyboardProc( ). hkb stores the handle of the hook installed.
From now on whenever a keyboard message is retrieved by the OS from the System Message Queue the message is firstly passed to our hook procedure, i.e. to KeyboardProc( ) function. Inside this function we have written code to ensure that the CapsLock always remains on. To begin with we have checked whether nCode parameter is less than 0. If it so then it necessary to call the next hook procedure. The MSDN documentation suggests that “if code is less than zero, the hook procedure must pass the message to the CallNextHookEx( ) function without further processing and should return the value returned by CallNextHookEx( )”.
Note that there can be several hook procedures installed by different programs, thus forming a chain of hook procedures. These hook procedures always get called in an order that is opposite to their order of installation. This means the last hook procedure installed is the first one to get called.
If the nCode parameter contains a value HC_ACTION it means that the message that was just removed form the system message queue was a keyboard message. If it is so, then we have checked the previous state of the key before the message was sent. If the state of the key was ‘depressed’ (30th bit of lParam is 1) then we have obtained the state of the CapsLock key by calling the GetKeyState( ) API function. If it is off (0th bit of state variable is 0) then we have turned on the CapsLock by simulating a keypress. For this simulation we have called the function keybd_event( ) twice—first call is for pressing the CapsLock and second is for releasing it. Note that keybd_event( ) creates a keyboard message from the parameters that we pass to it and posts it into the system message queue. The parameter VK_CAPITAL represents the code for the CapsLock key. One last point about this program—the ‘hook.dll’ file should be copied into the directory of the application’s EXE before executing the EXE
A word of caution! When we use keybd_event( ) to post keyboard message for a simulated CapsLock keypress, once again our hook procedure would be called when these messages are retrieved from the system message queue. But this time the CapsLock would be on so we would end up passing control to the next hook procedure through a call to CallNextHookEx( ).
When we close the application window as usual the OnDestroy( ) would be called. In this handler we have obtained the address of the removehook( ) exported function and called it. In the removehook( ) function we have unregistered our hook procedure by calling the UnhookWindowsHookEx( ) API function. Note that to this function we have passed the handle to our hook. As a result our hook procedure is now removed from the hook chain. Hereafter the CapsLock would behave normally. Having unhooked our hook procedure the control would return to OnDestroy( ) handler where we have promptly unload the DLL from memory by calling the FreeLibrary( ) API function.
No comments:
Post a Comment