Click here to Skip to main content
15,896,606 members
Articles / Programming Languages / C++

Journaling Input Events

Rate me:
Please Sign up or sign in to vote.
4.84/5 (17 votes)
3 Mar 2001 135.7K   4K   54  
A tool for recording and playback of keyboard and mouse input
// HookService.cpp
//

#include "stdafx.h"
#include "malloc.h"

#include "HookService.h"

HHOOK		g_hHookRecorder= NULL;
HHOOK		g_hHoohPlayBack= NULL;

			S_ENODE *g_lpEventChain	= NULL;  // Head of recorded Event List
static	S_ENODE *g_lpLastEvent	= NULL;  // Tail of recorded Event List
static	S_ENODE *g_lpPlayEvent	= NULL;  // Current Event being played

static	HWND		g_hWndManager	= NULL;
static	BOOL		g_fNoMouse		= FALSE;
static	BOOL		g_fDoClear		= FALSE;
static	BOOL		g_fSysModalOn	= FALSE;

LRESULT CALLBACK JournalPlaybackFunc(int nCode, WPARAM wParam, LPARAM lParam );
LRESULT CALLBACK JournalRecorderFunc(int nCode, WPARAM wParam, LPARAM lParam );

///////////////////////////////////////////////////////////////////////////////////

void fnFreeAllEvents()
{
	while(g_lpEventChain) {
		S_ENODE* p= g_lpEventChain->lpNext;
		free(g_lpEventChain);
		g_lpEventChain = p;
	}
	g_lpLastEvent = g_lpPlayEvent = NULL;
}

int fnStartRecorder(HWND hWnd,BOOL fNoMouse)
{
	if(g_hHookRecorder != 0)
		return -1;

	fnFreeAllEvents();	// zeroes g_lpEventChain, g_lpLastEvent, g_lpPlayEvent

	g_hWndManager	= hWnd;
	g_fSysModalOn	= FALSE;
	g_fNoMouse		= fNoMouse;

	g_hHookRecorder= SetWindowsHookEx(WH_JOURNALRECORD, JournalRecorderFunc, 
													AfxGetInstanceHandle(), 0);

	return 0;
}

int fnStop_Recorder(int nNotifyCode)
{
	if(g_hHookRecorder == 0)
		return -1;

	UnhookWindowsHookEx(g_hHookRecorder);
	
	g_hHookRecorder = NULL;

	g_lpLastEvent = g_lpPlayEvent = NULL;

	if(nNotifyCode != 0)
		SendMessage(g_hWndManager, WM_SERVICE_INFO, SI_E_RC_STOP, nNotifyCode);

	return 0;
}

int fnStartPlayBack(HWND hWnd,BOOL fDoClear)
{
	if(g_hHoohPlayBack != 0)
		return -1;

	g_hWndManager	= hWnd;
	g_fSysModalOn	= FALSE;
	g_fDoClear		= fDoClear;

	g_lpLastEvent	= g_lpPlayEvent = NULL;

	g_hHoohPlayBack= SetWindowsHookEx(WH_JOURNALPLAYBACK, JournalPlaybackFunc, 
													AfxGetInstanceHandle(), 0);

	return 0;
}

int fnStop_PlayBack(int nNotifyCode)
{
	if(g_hHoohPlayBack == 0)
		return -1;
	
	UnhookWindowsHookEx(g_hHoohPlayBack);
	
	g_hHoohPlayBack = NULL;

	if(g_fDoClear)
		fnFreeAllEvents();

	if(nNotifyCode != 0)
		SendMessage(g_hWndManager, WM_SERVICE_INFO, SI_E_PB_STOP, nNotifyCode);

	return 0;
}

LRESULT CALLBACK JournalRecorderFunc(int nCode, WPARAM wParam, LPARAM lParam)
{
	if(!g_hHookRecorder)
		return 0;

	if(nCode == HC_SYSMODALON)
		g_fSysModalOn = TRUE;
	else
	if(nCode == HC_SYSMODALOFF)
		g_fSysModalOn = FALSE;
	else
	if(nCode == HC_ACTION && g_hHoohPlayBack) { // Skip recording while playing back
		
		fnStop_Recorder(SI_E_RC_SC_BUSY);
		
		return 0;
	}

	if(nCode == HC_ACTION && !g_fSysModalOn) {

		LPEVENTMSG lpEvent= (LPEVENTMSG)lParam;

		// STOP - user stops recording by pressing CTRL+BREAK
		if(lpEvent->message == WM_KEYDOWN && LOBYTE(lpEvent->paramL) == VK_CANCEL) {
			
			fnStop_Recorder(SI_E_RC_SC_DONE);
			
			return 0;
		}
		
		S_ENODE *lpEventNode= (S_ENODE*)malloc(sizeof(S_ENODE));

		if(lpEventNode == NULL) {	// Not enough memory

			fnStop_Recorder(SI_E_RC_SC_MEMORY);

			return 0;
		}

		if(g_lpLastEvent == NULL )
			g_lpEventChain = lpEventNode;
		else
			g_lpLastEvent->lpNext = lpEventNode;

		g_lpLastEvent = lpEventNode;
		g_lpLastEvent->lpNext = NULL;

		g_lpLastEvent->Event.message	= lpEvent->message;
		g_lpLastEvent->Event.paramL	= lpEvent->paramL;
		g_lpLastEvent->Event.paramH	= lpEvent->paramH;
		g_lpLastEvent->Event.time		= GetTickCount();//lpEvent->time;
		g_lpLastEvent->Event.hwnd		= NULL;

		// inform the manager about the message recorded
		PostMessage(g_hWndManager, WM_SERVICE_INFO, SI_S_RC_EVENT, (LPARAM)g_lpLastEvent);
		
		return 0;
	}

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

LRESULT CALLBACK JournalPlaybackFunc (int nCode, WPARAM wParam, LPARAM lParam )
{
	static S_ENODE *lpPrevPlayedEvent;
   static DWORD dwLastEventTime;
	static DWORD dwDelay= 0;

	if(!g_hHoohPlayBack)
		return 0;

	if(nCode == HC_SYSMODALON)
		g_fSysModalOn = TRUE;
	else
	if(nCode == HC_SYSMODALOFF)
		g_fSysModalOn = FALSE;
	else
   if(nCode >= 0) {

		if(g_lpEventChain == NULL) {	// No any recorded records
			
			fnStop_PlayBack(SI_E_PB_SC_EMPTY);
			
			return 0;
		}

		if(g_hHookRecorder) {	// Recording is running - abort playing
			
			g_fDoClear = TRUE;	// To prevent recorded events to be deleted
			
			fnStop_PlayBack(SI_E_PB_SC_BUSY);
			
			return ( (int)CallNextHookEx(g_hHoohPlayBack, nCode, wParam, lParam));
		}

      if(g_lpPlayEvent == NULL) {	// First event is being played
			
			dwDelay = 0;
			
			g_lpPlayEvent	= g_lpEventChain;
			g_lpLastEvent	= NULL;	   // For the next time we start the recorder
			
			dwLastEventTime= g_lpPlayEvent->Event.time;

			lpPrevPlayedEvent = NULL;
			
			PostMessage(g_hWndManager, WM_SERVICE_INFO, SI_S_PB_START, (LPARAM)dwLastEventTime);
      }

      if(nCode == HC_SKIP) {

			if(g_lpPlayEvent->lpNext == NULL) {	// done recording

				if(g_fDoClear) {
					free(g_lpEventChain);
					g_lpEventChain= NULL;
				}
				
				g_lpPlayEvent = g_lpLastEvent = NULL;

				fnStop_PlayBack(SI_E_PB_SC_DONE);

			} else {
				
				dwDelay ++;
				if(dwDelay == 0)
					dwDelay = 1;

				dwLastEventTime= g_lpPlayEvent->Event.time;
				g_lpPlayEvent	= g_lpPlayEvent->lpNext;
				
				if(g_fDoClear) {
					free(g_lpEventChain);
					g_lpEventChain = g_lpPlayEvent;
				}
				PostMessage(g_hWndManager, WM_SERVICE_INFO, SI_S_PB_SKIP, 0);
			}

			return 0;

		} else 
		if(nCode == HC_GETNEXT) {
	  
			LPEVENTMSG lpEvent= (LPEVENTMSG)lParam;

			lpEvent->message = g_lpPlayEvent->Event.message;
			lpEvent->paramL  = g_lpPlayEvent->Event.paramL;
			lpEvent->paramH  = g_lpPlayEvent->Event.paramH;
			lpEvent->time    = g_lpPlayEvent->Event.time + GetTickCount();

			long lReturnValue= 0;
			
			if(dwDelay) {

				dwDelay = 0;

				lReturnValue = g_lpPlayEvent->Event.time - dwLastEventTime;

				if(lReturnValue < 0L)
					lReturnValue = 1L;
			}

			if(lReturnValue != 0)
				PostMessage(g_hWndManager, WM_SERVICE_INFO, SI_S_PB_WAIT, lReturnValue);
			else
			if(lpPrevPlayedEvent != g_lpPlayEvent) {
				PostMessage(g_hWndManager, WM_SERVICE_INFO, SI_S_PB_PLAY, (LPARAM)g_lpPlayEvent->Event.message);
				lpPrevPlayedEvent = g_lpPlayEvent;
			}

			return lReturnValue;
      }
	}

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

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer
Bulgaria Bulgaria
see www.geocities.com/plamen_petrov2000

Comments and Discussions