Hooking the Keyboard

WEBINAR: On-demand webcast

How to Boost Database Development Productivity on Linux, Docker, and Kubernetes with Microsoft SQL Server 2017 REGISTER >

Environment: VC6 , Windows 2000/NT/ME/9x

This article describes how to install a Keyboard hook in Microsoft Windows.

There are two types of Hooks - Thread specific hooks and Systemwide hooks. A thread specific hook is associated with particular thread only (Any thread owned by the calling process.). If you want to associate the hook with other processes and threads, you will have to use a systemwide hook. There is a hook procedure associated with a hook. This procedure is always called when the particular event occurs. For eg. the mouse. When there is an event associated with the mouse, this hook procedure is called. The hook is set by calling the function SetWindowsHookEx( ). The hook is removed by calling UnhookWindowsHookEx( ).

For thread hooks, the hook procedure may be in an EXE file or a DLL. But for Global or System hooks, the hook procedure must reside in a DLL. For this, we need to create a DLL.

To do this, create a Win32 DLL project with only the starter files in it and modify it to suit your needs. It is better to put the code for installing and removing the hook in the DLL itself.

Now, define the functions in the DLL's header file as follows.

#ifdef KEYDLL3_EXPORTS
#define KEYDLL3_API __declspec(dllexport)
#else
#define KEYDLL3_API __declspec(dllimport)
#endif

//This function installs the Keyboard hook:
KEYDLL3_API void installhook(HWND h);

//This function removes the previously installed hook.
KEYDLL3_API void removehook();

//hook procedure:
KEYDLL3_API LRESULT CALLBACK hookproc( int ncode,
                                       WPARAM wparam,
                                       LPARAM lparam);

For exporting the functions in the DLL, it is a good idea to use the __declspec and dllexport keywords rather than using a separate .DEF file. The SetWindowsHookEx( ) function returns a handle to a hook - which is stored for later uninstall of the hook from the hook chain. We also have a window handle, which we will use to send messages to the main Application Window. We first find the application window by using the FindWindow( ) function, and then send the keystroke message parameters to the Application's main window using the PostMessage( ) call. This is as in the code fragment below:

//Find application window handle
hwnd = FindWindow("#32770","Keylogger Exe");

//Send info to app Window.
PostMessage(hwnd,WM_USER+755,wparam,lparam);

At the end of the hook procedure, we must call the CallNextHookEx( ) function to pass on the parameters to the next hook installed in the hook chain. This is highly recommended because not doing so can cause unpredictable system behaviour and lockouts. The procedures for installing, removing the hooks, and the hook procedure are as shown below:

KEYDLL3_API void installhook(HWND h)
{
  hook = NULL;
  hwnd = h;
  hook = SetWindowsHookEx( WH_KEYBOARD,
                           hookproc,
                           hinstance,
                           NULL);
  if(hook==NULL)
    MessageBox( NULL,
                "Unable to install hook",
                "Error!",
                MB_OK);
}

KEYDLL3_API void removehook()
{
  UnhookWindowsHookEx(hook);
}

KEYDLL3_API LRESULT CALLBACK hookproc( int ncode,
                                       WPARAM wparam,
                                       LPARAM lparam)
{
  if(ncode>=0)
  {
     //Find application window handle
     hwnd = FindWindow("#32770","Keylogger Exe");
     //Send info to app Window.
     PostMessage(hwnd,WM_USER+755,wparam,lparam);
  }
  //pass control to next hook.
  return ( CallNextHookEx(hook,ncode,wparam,lparam) );
}

If there are multiple instances of the DLL in memory, they all have different values for each data member of the different DLL instances. But, certain data, such as the hook handle, the window handle should be the same for all instances. This is because all instances send info to the same Application window. For this, we need to define the data as shared in the DLL's .CPP file. This is done as follows:

#pragma data_seg(".HOOKDATA")//Shared data among all instances.
HHOOK hook = NULL;
HWND hwnd = NULL;
#pragma data_seg()

Now, the linker must be given instructions so as to place the shared data in separate space in the DLL. To do this, we use the following code, soon after the abovementioned code.

//linker directive
#pragma comment(linker, "/SECTION:.HOOKDATA,RWS")

So much for the DLL. Now, we will take a look at the Main application(EXE). Create an MFC application (windowed or dialog based). I created a Dialog based EXE for simplicity. After creating the project, Go to Project settings dialog box by selecting Project>Settings from the Main Menubar. Select the 'Link' tab and type 'Keydll3.lib' in the 'Object/library modules' box. Click OK. Now, Insert the DLL's header file into the workspace by selecting Project>Add to project> files from the main menubar. Select the .h file of the DLL that we built earlier. '#include' it in your project as follows:

//Include this for functions in the DLL:
#include "..\Keydll3\Keydll3.h"

This should be in the .CPP file for the Main dialog class. Now, in the main dialog class, add a public member function to process the keystroke messages sent by the DLL. This function is as shown below:

afx_msg LRESULT processkey(WPARAM w,LPARAM l);//declaration 

LRESULT CKeyexeDlg::processkey(WPARAM w, LPARAM l)//definition
{
  //This block processes the keystroke info.
  .
  .
  .
  return 0L;
}

(This member can be easily added using the wizardbar). Now, define the Message we shall receive from the DLL in the .CPP file as follows:

//This message is recieved when key is down/up
#define WM_KEYSTROKE (WM_USER + 755)

Now add the newly created member function as the message handler for the WM_KEYSTROKE message, using the ON_MESSAGE( ) macro in the Message maps section(in the CPP file) as below: \

BEGIN_MESSAGE_MAP(CKeyexeDlg, CDialog)
  //{{AFX_MSG_MAP(CKeyexeDlg)
    .
    .
    .
  ON_MESSAGE(WM_KEYSTROKE, processkey)
  //}}AFX_MSG_MAP
END_MESSAGE_MAP()

We are almost finished. But, before compiling and building the EXE, add the path to the .LIB file (Keydll3.lib) to the Visual studio Library paths. To do this, select Tools>Options from the main menubar and select the 'Directories' tab. Select 'Library files' from the 2nd dropdown list, and add the path to the DLL's .LIB file in the box below. Click OK, Save all files and workspace, and then build your project.

For more information on Hooks, see the following sections in the MSDN library:

  • SetWindowsHookEx( ),
  • Hook functions,
  • Virtual-key codes,
  • Keystroke message flags.

The example I have used here is provided for download (See below).

Note: For Windows NT/2000, your windows password might be logged by the hook procedure if you have not enabled the 'Ctrl-Alt-Del' logon sequence(Only while unlocking the PC).

Downloads

Download demo project - 69 Kb
Download source - 25 Kb


Comments

  • data_seg && comment(linker...)

    Posted by Thatoneguy on 09/12/2014 08:01pm

    What exactly is the data_seg and comment(linker, "/SECTION:.HOOKDATA,RWS") doing? Could you explain it in laymans terms? I've never seen these used as I have little DLL experience

    Reply
  • Keys Sent Infiniate Number of Times to hookproc()

    Posted by Richard01 on 10/20/2009 09:21pm

    I'm having a problem where hookproc() gets called indefinitely and is repeatedly sent the same key. Is this happening for other people or just me? I'm building the project with Visual Studio 2008 and running it on Windows XP SP3.

    Reply
  • Help: Mutiple keys on Microsoft office

    Posted by bandicoot on 11/18/2007 09:14pm

    First of all, great piece of code ^^ but having some problems with Micosoft Office and several other applications. There are multiple keys detected for a single keystroke, meaning when i type 'a' the report will give me 'aaaaaa' Why does this happen and how can I stop it from happening?
    Thank you..

    Regards,
    bandicoot

    Reply
  • How to hook keyboard events from other processes ?

    Posted by udir on 10/11/2006 04:05am

    How to hook keyboard events from other processes ? Althought I set the 4th parameter in: hook = SetWindowsHookEx( WH_KEYBOARD,hookproc,hinstance,NULL); to NULL, I opened word and clicked. No event was hooked. Any ideas ? Thanks, Udi

    Reply
  • New version will be released

    Posted by anoopt on 09/28/2006 04:07am

    Hi, I will be releasing a new version soon, with other additions and improvements...

    Reply
  • two key on the same time jams the application

    Posted by o.k. on 04/03/2006 08:27am

    two key on the same time jams the application. can anybody give me a solution so pressing two keys on the same time will not jam the application?

    • another detail / partial solution

      Posted by o.k. on 04/05/2006 08:55am

      if forcing a delay between every two following messages from the callback (using time measurement) the problem is solved. //[Filter hook function] static LRESULT CALLBACK msghook(UINT nCode, WPARAM wParam, LPARAM lParam) { static double tmrStart, tmrElapsed; if(nCode < 0) { // pass it on CallNextHookEx(hook, nCode, wParam, lParam); return 0; } // pass it on LPMSG msg = (LPMSG)lParam; if ((lParam & 0x40000000) == 0x00000000) { tmrElapsed = GetCurrentTime() - tmrStart; tmrStart = GetCurrentTime(); if (tmrElapsed>10) PostMessage(hWndServer, UWM_MOUSEMOVE, wParam, lParam); } return CallNextHookEx(hook, nCode, wParam, lParam); } //[End Filter hook function] can anybody sagest a way to verify a previous Message was processed before sending a new message? ( I donbt like using static / global time flags to resolve collisions.)

      Reply
    Reply
  • VB

    Posted by lordnephilim on 03/03/2005 11:23am

    can this dll be included in a VB project, and how?

    Reply
  • Anyone Know THIS?

    Posted by Legacy on 02/26/2004 12:00am

    Originally posted by: JT

    I am using this... hook dies after the window loses focus.

    Reply
  • Easy to understand

    Posted by Legacy on 02/23/2004 12:00am

    Originally posted by: Srinivas

    Great Work ,your article helped me a lot,can you tell me ,is there any way to Check whether key was pressed(not released).
    other than
    if((lparam & 0x80000000) == 0x00000000)


    anyhow ,Great Work!

    Reply
  • hooking remote keyboad ?

    Posted by Legacy on 02/05/2004 12:00am

    Originally posted by: kiran

    how this can be done or not possible , i.e without installing any client program

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • As all sorts of data becomes available for storage, analysis and retrieval - so called 'Big Data' - there are potentially huge benefits, but equally huge challenges...
  • The agile organization needs knowledge to act on, quickly and effectively. Though many organizations are clamouring for "Big Data", not nearly as many know what to do with it...
  • Cloud-based integration solutions can be confusing. Adding to the confusion are the multiple ways IT departments can deliver such integration...

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date