Home
Tutorials
Code Snippets
Code Samples
Downloads
Links

The Blog
Our Projects
About
Contact

::Add RageStorm to Favorites!::

The Blog | Our Projects | Guest Book | About | Contact

 
Tutorial - Keyboard Hook
Author:Arkon
Category:Win32API
Uploaded at:29-May-03
Views:77242
Hook is a mechanism, by which a function can intercept events before they reach an application. The function can act on events, modify or discard them. Functions which receive the events are called Filter Functions, every Filter Function is classified by its type. Hooks provide powerful capabilities: Procces or modify every message; Record or play back keyboard and mouse events; Prevent another filter from being called; And many more capabilities... Generally, there are two types of hooks: System-wide, and Thread-specific. The System-wide hook is used for filtering messages of all applications(IE: when writing a key logger). And the Thread-specific hook is used for filtering messages of a specific thread. In this tutorial, I'll cover just System-wide keyboard hook. To set a System-wide hook we need a DLL.

A DLL is an indirectly executable which doesn't have a message loop to receive user input. DLLs are seperate files containing functions(not only) that can be called by programs and other DLLs. To the end-user a DLL is a program that can't be executed directly from the Program Manger(or other Shells), but from the system's point of view, there are two differences between DLLs and applications: DLLs cannot have multiple running instances loaded in memory. and DLLs attach themselves to processes, only application instances are processes. DLL stands for Dynamic-Link Library. Dynamic-Link is a mechanism to link libraries to applications at run time. These libraries(DLLs) reside in their own executable files(.dll) and are not copied into applications' executable files(.exe) as with Static-Link libraries. It's important to understand that a .DLL is loaded into the address space of the specified linking application and not into the global heap! The advantages of using dynamic linking method are:
They can be updated without requiring applications to be recompiled or relinked.
When several applications use the same .DLL, the .DLL is just loaded once for all applications(reducing memory and disk space).

The reason we need a DLL for a System-wide hook is because we want the Filter Function to be in any application address space. So when you set the hook message filter function which lies in the .dll Windows maps the .dll automatically into all applications' address space. Thus you get your filter function called for every process! Therefore when we dynamic linking the hook which is in a .DLL it becomes a System-wide hook(ofcourse it depends on the type of Filter Function too). Okay, now after the important theoretical material..
Let's start coding the .DLL for the System-wide keyboard hook, and then I'll explain how to dynamic linking the .DLL into our application.
Please download the source code in order to see how it's all written and done.

// This is our keyboard callback proc:
// _declspec(dllexport) means that we want to let other
// applications/DLLs call this function, in same cases you don't
// need this prefix, but for safety we'll use it.
_declspec(dllexport) LRESULT CALLBACK KBHookProc(int Code, WPARAM wParam, LPARAM lParam)
{
 // If code is less than 0 we call the next hook,
 // this is how hooks work, nothing special
 if (Code < 0) return(CallNextHookEx(hhook, Code, wParam, lParam));

 // Info about what happens with the keyboard
 if (lParam & (1 << 31)) // Bit 31 tells whether a key is down

 // Call the next hook.
 return(CallNextHookEx(hhook, Code, wParam, lParam));
}

// This is same as WinMain/Main but for DLLs, 
// you get the DLL instance, Reason of how your .DLL is called, reserved.
BOOL APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
// There are actually 4 reasons, we'll process just 2 of them,
// because it's enough for our prog.
 if (dwReason == DLL_PROCESS_ATTACH) // Loading the .DLL
 {
  if(pcount == 0)
  {
   // Init // First loading time
   // Set hook..
  }
  pcount++;
 }
 else if (dwReason == DLL_PROCESS_DETACH)
 {
  // UnInit before the system frees the .DLL from memory.
  if(pcount == 1)
  {
   // Free
   // Kill hook..
  }
  pcount--;
 }
 return(1);
}

You see, it's pretty easy, I would say. Anyways, pcount is our process counter, it says how many times the .DLL has been called. pcount is a special variable, it has to be in the global heap, because if it won't then everytime our .DLL is called it will be initialized to 0 again and again...so in order to prevent that, we tell the compiler to put the variable in the global heap. We do that for the hook handle too, because we set the hook only once. If we set the hook everytime the .DLL is called, it will be a messed hook and it wouldn't work properly and as we wish! In order to tell the compiler we want it to put the variable in global heap we do this:

#pragma data_seg("SharedBlock")
HHOOK hhook = NULL;
int pcount = 0;
#pragma data_seg()

Notice every variable has to be initialized. ok we are going to HHOOK hhook = NULL; unsigned long keystrokes = 0; int pcount = 0; #pragma data_seg() We soon finish writing the .DLL, every .DLL file has to have a .DEF file to direct the compiler. In the .DEF file we tell the compiler the functions we want to export, yes, those with the _declspec(dllexport), and we tell the compiler about the shared data block. (See the source code for full understanding the .DEF file). Once the .DLL file is ready, you have to copy it to the application which loads the .DLL directory or to copy it to %windir%\system.

OK, that's it for the .DLL stuff, now let's load the .DLL we just created! First we have to declare pointers to functions with the parameters they receive.

typedef void (*LPFunction)(void);
LPFunction function = NULL;

// This is a pointer to a function which has no
// parameters and which returns a void.

 HMODULE DLLmodule = LoadLibrary("mydll.dll");
 if (!DLLmodule) // .DLL file is not found!

 // This function gets the code of the specific wanted function from memory,
 // Notice the upper case letters, the wanted function name is to be exactly,
 // as in the .DEF file and .CPP file of the DLL we wrote.

 function = (LPFucntion)GetProcAddress(DLLmodule, "Function");
 if (!function) // Function implementation code is not found!

 // Now you can call function.
 function();

 // After finish using the .DLL, don't forget to free it!
 FreeLibrary(DLLmodule);

Woohoo, that's it!! Well, this was just the basics and it won't work if you copy paste, so download the source code!

Source Code:
Visual C++ 6 - kbhook.zip

User Contributed Comments(7)
 [1] arkon | 2005-10-17 21:33:41

if you install the low level keyboard hook, you won't necessariliy need a .DLL,
and it will be a system wide hook, thus you will intercept all keypressings in all applications.
(That's for XP, y2k/3.)

SetWindowsHookEx(WH_KEYBOARD_LL, (HHOKPROC)FilterFunc, GetModuleHandle(NULL), 0);
 [2] Lachhaman | 2006-05-12 21:46:55

This is a good one for a newbie like me. I was badly looking for such articles and came across this. I might have loved to see a bit focus on how to modify messages before it reaches the application.

Thanks
Lachhaman
 [3] Jonathan | 2006-10-17 12:06:13

#include <windows.h>

char szClassName [] = "szClassName";
char szWindowName [] = "vertulion";

HWND hWndMain;

LRESULT CALLBACK MainWndProc (HWND, UINT, WPARAM, LPARAM);

LRESULT CALLBACK KeyboardProc (int, WPARAM, LPARAM);

/*
*/

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX wcex;
    wcex.cbClsExtra = 0;
    wcex.cbSize = sizeof (WNDCLASSEX);
    wcex.cbWndExtra = 0;
    wcex.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH);
    wcex.hCursor = LoadCursor (NULL, IDC_ARROW);
    wcex.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wcex.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wcex.hInstance = hInstance;
    wcex.lpfnWndProc = MainWndProc;
    wcex.lpszClassName = szClassName;
    wcex.lpszMenuName = NULL;
    wcex.style = CS_HREDRAW | CS_VREDRAW;

    if (!RegisterClassEx (&wcex))
        return 0;

    hWndMain = CreateWindowEx (NULL,
        szClassName,
        szWindowName,
        NULL,
        GetSystemMetrics (SM_CXSCREEN) / 2 - 320 / 2,
        GetSystemMetrics (SM_CYSCREEN) / 2 - 240 / 2,
        320,
        240,
        NULL,
        NULL,
        hInstance,
        NULL);

    if (hWndMain == NULL)
        return 0;

    //ShowWindow (hWndMain, nCmdShow);
    //UpdateWindow (hWndMain);

    MSG msg;

    while (GetMessage (&msg, NULL, 0, 0) > 0)
    {
        TranslateMessage (&msg);
        DispatchMessage (&msg);
    }

    return static_cast <int> (msg.wParam);
}

/*
HHOOK SetWindowsHookEx(int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId);

SetWindowsHookEx(WH_KEYBOARD_LL, (HHOKPROC)FilterFunc, GetModuleHandle(NULL), 0);
*/

HHOOK hKeyHook;

LRESULT CALLBACK MainWndProc (HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
    switch (uMessage)
    {
    case WM_CREATE:
        {
            hKeyHook = SetWindowsHookEx (13 /*WH_KEYBOARD_LL*/, (HOOKPROC) KeyboardProc, GetModuleHandle (NULL), 0);
        }
        break;

    default:
        {
            return DefWindowProc (hWnd, uMessage, wParam, lParam);
        }
        break;
    }

    return 0;
}

LRESULT CALLBACK KeyboardProc (int nCode, WPARAM wParam, LPARAM lParam)
{
    switch (nCode)
    {
    case HC_ACTION:
        {
            MessageBox (NULL, NULL, NULL, MB_OK);
            PostQuitMessage (0);
        }
        break;
    }

    return 0;
}
 [4] Yilang Yu | 2007-05-09 01:30:20

I download source codes and built arunhook.exe. But it returns a msg that the memory could not be "read". Am I missing some thing here. The full msg is:
The instruction at "0x00000000" referenced memory at "0x00000000". The memory could not be "read".

Please help!
Yilang
 [5] arkon | 2007-05-10 20:17:48

i just ran it successfully.
try running it in debug mode under a debugger too...and give more details about the problem.
 [6] Jonathan | 2007-07-20 04:14:38

Heh.. I lost the source code for that program, it ended up working very well.

I haven't tried it on Vista yet.
 [7] Pontus | 2009-11-24 22:16:01

Works on vista.
Compile BOTH projects, include the DLL in both directories of the build
NOTE:
Comments that will hurt anyone in any way will be deleted.
Don't ask for features, advertise or curse.
If you want to leave a message to the author use the contacts,
if you have any question in relation to your comments please use the forum.
Comments which violate any of these requests will be deleted without further
notice. Use the comment system decently.

Post your comment:
Name:
email:
Comment:
::Top 5 Tutorials::
Embedded Python[116851]
2D Rotated Rectangles Collision Detection[88945]
Keyboard Hook[77242]
UDP[65797]
HTTP Proxy[41187]

::Top 5 Samples::
2D ColDet Rotated Rectangles[11557]
PS2 Mouse Driver[6953]
Wave Format Player[5788]
Reading FAT12[5615]
CodeGuru[5355]


All rights reserved to RageStorm © 2009