Click here to Skip to main content
15,886,519 members
Articles / Programming Languages / C++

Recording mouse and keyboard events and playing them back

Rate me:
Please Sign up or sign in to vote.
3.73/5 (7 votes)
8 Jul 2007CPOL1 min read 67.9K   7.5K   37   5
The application records user input events into a file and plays them back when required.

Introduction

This is a sample project used to record keyboard and mouse events from a user in a file and to play them back when required.

Background

The program does not do anything tricky and just uses Windows hooks (the WH_JOURNALRECORD hook to be precise). You can refer to MSDN for more details.

Using the Code

The code is organized into two projects: the application that controls the start and stop of record and play events, the DLL which actually does the job.

For WH_JOURNALRECORD hooks, the DLL may not be required and the core logic can be embedded in the application source itself.

Following is how the application works: for starting recording: click File->Start recording, and then press the Start button on the menu. All the events get recorded in c:\recording.txt. To stop recording, click File->Stop recording.

To play back the recording, click File->Play and then press the Start button on the menu.

Following is the hook procedure that records the events:

C++
// this procedure gets called whenever windows wants us to record some event 
LRESULT CALLBACK RecorderCallProc(int nCode, WPARAM wParam, LPARAM lParam)
{
  EVENTMSG *msg_ptr = NULL;
  static int continue_recording = TRUE;
  
  switch(nCode)
  {
    case HC_SYSMODALON:  /* some system modal dialog is ON. Stop recording */
      continue_recording = FALSE;
      return CallNextHookEx(g_recorder_hook, nCode, wParam, lParam);

    case HC_SYSMODALOFF: /* system modal dialog is OFF, continue recording */
      continue_recording = TRUE;
      return CallNextHookEx(g_recorder_hook, nCode, wParam, lParam);
  }

  if (nCode != HC_ACTION || continue_recording == FALSE)
  { 
  /* Record only on HC_ACTION event when recording is enabled; not now */
    return CallNextHookEx(g_recorder_hook, nCode, wParam, lParam);
  }

  msg_ptr = (EVENTMSG *)lParam;
  
  /* Store the event passed by windows in a file to read back later for play */
  fwrite(msg_ptr, sizeof(EVENTMSG), 1, g_fp);
  fprintf(g_recorder_log, "HWND:%d, MSG:%d, WPARAM:%d, LPARAM:%d\n", 
          msg_ptr->hwnd, msg_ptr->message, 
          msg_ptr->paramH, msg_ptr->paramL);

  return CallNextHookEx(g_recorder_hook, nCode, wParam, lParam);
}

// This proc gets called to fetch next event to play 
__declspec(dllexport) LRESULT CALLBACK PlayerProc(int nCode, WPARAM wParam, LPARAM lParam)
{
  static FILE *fp = NULL;
  static int continue_playing = TRUE;
  static EVENTMSG msg;
  int ret_val = 0;
  static int previous_timeout = 0;
  static int current_timeout = 0;
  int timeout = 0;

  if (fp == NULL)
  { /* The events recorded in procedure below are to be read back now for playing */
    fp = fopen("c:\\recording.txt", "rb");
    ret_val = fread(&msg, sizeof(EVENTMSG), 1, fp);
    previous_timeout = msg.time;
    current_timeout = msg.time;
  }

  switch(nCode)
  {
    case HC_SYSMODALON:  /* some system modal dialog ON. Pause playing */
      continue_playing = FALSE;
      return 0;

    case HC_SYSMODALOFF: /* system modal dialog OFF. continue playing */
      continue_playing = TRUE;
      return 0;

    case HC_SKIP:  /* time to send next event in file to windows */
      if (continue_playing == FALSE)
      {
        return 0;
      }

      ret_val = fread(&msg, sizeof(EVENTMSG), 1, fp);
      fprintf(g_player_log, "HWND:%d, MSG:%d, WPARAM:%d, LPARAM:%d\n", msg.hwnd, msg.message, \
         msg.paramH, msg.paramL);
      previous_timeout = current_timeout;
      current_timeout = msg.time;

      if (ret_val != 1)
      {
        /* End of file. Uninstall the hook */
        fclose(fp);
        fclose(g_player_log);
        fp = NULL;
        UnhookWindowsHookEx(g_player_hook);
        MessageBox(NULL, TEXT("Recording complete"), TEXT("Success"), 0);
        
      }
      return 0;
  }

  if (nCode != HC_GETNEXT || continue_playing == FALSE)
  {
    if (nCode < 0)
    {
      return CallNextHookEx(g_player_hook, nCode, wParam, lParam);
    }
    return 0;
  }
  /* Copy the current event to lparam if action is HC_GETNEXT */
  memcpy((void *)lParam, (void *)&msg, sizeof(msg))


  /* if function returns 0 on HC_GETNEXT, then the event stored in lparam gets
executed immediately. Else, windows sleeps for that many milliseconds and asks
for the same event again. The timestamp of the events is also stored. Use
those to raise an event at appropriate time.*/
  if (current_timeout != previous_timeout)
  {
    timeout = (current_timeout > previous_timeout ? (current_timeout - previous_timeout) : \
     (current_timeout + (0xffffffff - previous_timeout)));
    previous_timeout = current_timeout;
    return timeout;
  }
  else
  {
    return 0;
  }
}

Points of Interest

The program is a very naive application. I found that the following events do not get recorded:

  1. Pressing the Windows key
  2. Typing anything in the Run command (Start->Run)
  3. Windows Search wizard

The Start button is additionally placed in the window so that the mouse pointer is on the same location when starting a recording and when playing a recording. Else, a slight difference in position can cause great havoc (when Windows starts clicking on wrong (adjacent) windows during playback).

History

Initial draft.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionError - Could not Create the Hook Pin
jayesh baviskar13-Sep-18 19:38
jayesh baviskar13-Sep-18 19:38 
QuestionDemo not working Pin
rahuljaw10-Jan-17 1:26
rahuljaw10-Jan-17 1:26 
Generalapp to record Test cases and record mouse events Pin
Member 1172725331-May-15 18:11
Member 1172725331-May-15 18:11 
QuestionCode Pin
Hari Haran18-May-14 18:49
Hari Haran18-May-14 18:49 
GeneralVista Pin
B4stard9-Aug-07 16:28
B4stard9-Aug-07 16:28 
How do I rewrite this to work under Vista, given that journal hooks no longer work? I only want to be able to capture and play back user input within my own application.
I've read that the hooks will work if the application is signed + has a manifest + is installed under program files, but that isn't going to happen.
Can it be done using WH_KEYBOARD and WH_MOUSE hooks?

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.