Oct 1, 2009

Journal Hook

Following last Windows focused post, I come to talk you about hooks. Well, I'm not an expert about hooks, but basically, a Windows' hook is a procedure that you can register in the operating system to intercept events before they reach an application. More information in msdn.



Well, in our try to automatize systems, we need to create a mechanism to intercept all mouse and keyboard messages generated by user in desktop. So, we can record something similar to macros in Windows, and later playback them. For example, we need to automate the Skype user interaction, or the Outlook application, and then play the macro.

It's a very interesting thing because, WM_MOUSEMOVE is a message too ;), and we could save it for example to perform some QA automated tests in a game, moving the avatar around scenario, capturing mouse moving and keyboard strokes. There are a lot of applications about hooks, and there are more hooks than only Journaling which is only a kind of hook.

Here you have a very simple application that creates this hook and records all messages. Just a note: Be careful when you're executing from Visual Studio IDE, because if you set a breakpoint in code, the hook cannot be established correctly. Maybe it could be generated by VS thread (attached to process) which avoid messages to hook, I don't know, but it is a very rare bug, difficult to find, and can drives a team's member crazy. In fact, done it.

The idea behind, you intercept all messages and serializes them (lparam, wparam, time, message number, even the hwnd but have no sense) and when you want play them you've to feed to journal playback hook with those messages. In the code below, (all) the messages are stored in a plain txt file, but its performance can be pretty bad due to the big number of messages intercepted. You can buffer and then flush them. Also you can change this, for example, sending them by network or something. Let your imagination flies.

Finally, it is not a new thing, you can google for "winmacro" and you'll find an open software (with source code) which does this journaling stuff, but with an own interface and more control. Anyway, in my code below you only have a record feature. Playback in another post...

CPP file here.


#include <windows.h>
#include <stdio.h>

// -- wow, two global objects!
static HHOOK gHookHandle = 0;
static FILE* gFile = NULL;

LRESULT CALLBACK JournalRecordProc(int code ,WPARAM wparam,LPARAM lparam)
{
if ( code == HC_ACTION )
{
EVENTMSG* mesg = (EVENTMSG *)lparam;
// -- Serializes entire message
fprintf_s( gFile, "%d %d %d %d %d\n", mesg->message, mesg->paramL, mesg->paramH, mesg->time, (UINT)mesg->hwnd );
// -- Exiting when PAUSE key is pressed
if ( mesg->message == WM_KEYDOWN && GetAsyncKeyState(VK_PAUSE) )
{
const LRESULT result = CallNextHookEx( gHookHandle, code, wparam, lparam);
UnhookWindowsHookEx( gHookHandle );
gHookHandle = 0;
PostQuitMessage(0);
return result;
}
}
return CallNextHookEx( gHookHandle, code, wparam, lparam );
}


// WinMain
// --------
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
// -- Record hook and file to store messages
if ( (gHookHandle = SetWindowsHookEx(WH_JOURNALRECORD, JournalRecordProc, hInstance, 0) ) == NULL )
return 1;
if ( (gFile = fopen( "msgs.txt", "wt" ) ) == NULL )
return 1;
fprintf_s( gFile, "# Message ParamL ParamH Time HWND\n" );

// -- Main message dispatching
MSG msg = {0};
GetMessage(&msg, 0, NULL, NULL );
while ( msg.message != WM_QUIT )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
GetMessage(&msg, 0, NULL, NULL );
}

// -- Closing
fclose( gFile );
return 0;
}

0 comments: