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:
LRESULT CALLBACK RecorderCallProc(int nCode, WPARAM wParam, LPARAM lParam)
{
EVENTMSG *msg_ptr = NULL;
static int continue_recording = TRUE;
switch(nCode)
{
case HC_SYSMODALON:
continue_recording = FALSE;
return CallNextHookEx(g_recorder_hook, nCode, wParam, lParam);
case HC_SYSMODALOFF:
continue_recording = TRUE;
return CallNextHookEx(g_recorder_hook, nCode, wParam, lParam);
}
if (nCode != HC_ACTION || continue_recording == FALSE)
{
return CallNextHookEx(g_recorder_hook, nCode, wParam, lParam);
}
msg_ptr = (EVENTMSG *)lParam;
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);
}
__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)
{
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:
continue_playing = FALSE;
return 0;
case HC_SYSMODALOFF:
continue_playing = TRUE;
return 0;
case HC_SKIP:
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)
{
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;
}
memcpy((void *)lParam, (void *)&msg, sizeof(msg))
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:
- Pressing the Windows key
- Typing anything in the Run command (Start->Run)
- 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.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.