wiki:PlugIns/MFC/usagelog.dll

usagelog.dll

This tool is implemented as a Dynamic Linked Library (DLL) called usagelog.dll. The interface of the DLL is kept extremly simple, it provides only two functions: one to start the logging and one to end it. The logging itself is done, by utilizing the message hooks. Hooks are placed inside the message processing chain of a program. Using them, it is possible to inspect the internal messaging of a program without modifying the program itself. For the different kinds of message, different hooks are provided. Currently, two hooks seem to be sufficient: a WH_GETMESSAGE and a WH_CALLWNDPROC hook.

To be able to replay logged actions, three general informations are needed:

  1. The correct order of the events.
  2. A description of the event itself.
  3. The target of the event.

To identify the order of the event, the order of the logged messages is used. It is assumed that this logging order is correct and that therefore the events took also place in this order.

To describe the events themselves is more difficult. The logger has the responsibility to log any information that might be needed for event identification or replaying of events. To this aim, the messages are filtered based on their type, e.g., WM_CREATE and WM_COMMAND.

The target of the event is identified using the HWND of the GUI object that the event has been addressed to.

Build information

The project is developed with Visual Studio 2008 and should compile cleanly. If the linker crashes during incremental builds, simply build again. It will be successful the second time.

The logging protocol is selected using defines:

  • __USING_MTRACE__ can only be compiled in cooperation with software from Mahr and should therefore not be enabled.
  • __USING_COSTUMLOG__ writes a logfile "usagelog.txt" to the execution path
  • __ENCODE_BASE64__ defines that the log shall be encoded base64
  • __TIMING__ includes timing information, i.e., measurements how long the logging itself requires, to measure if the application is slowed down due to the logging.

Architecture

The architecture of the logging mechanism is rather simple. It is a DLL written in C++. The interface provides currently only functions to start and stop the logging:

__declspec(dllexport) void __cdecl InitUsagelog();
__declspec(dllexport) void __cdecl ReleaseUsagelog();

The message filter is defined in as a simple switch-statement and may only be modified at compile time.

As data format for the logs a simple XML scheme is used, described by the following DTD:

<!ELEMENT log (session*)>
<!ELEMENT session (msg*)>
<!ELEMENT msg (param*)>
<!ELEMENT param EMPTY>
<!ATTLIST msg
  type  CDATA #REQUIRED>
<!ATTLIST param
  name  CDATA #REQUIRED
  value CDATA #REQUIRED>

A log consists of several sessions, i.e., program executions. Every session consists of the messages it recorded. A message consists of a type and of parameters. The of a message is describes by an integer that identifies the windows message that has been send, e.g., WM_CREATE, WM_DESTROY or WM_LBUTTONCLICK. The parameters are name, value pairs, both stored as strings. Using the parameters, it is possible to store additional information to messages in a generic and flexible way. For example, in case a window is created additional information about the window, like its resource id.

How to use

To enable the logging the usagelog.dll has to be loaded:

HINSTANCE hinstDll = NULL; // Handle of the Dll. Should be global, as it needs to be used to start/stop the logging and to unload the Dll.

// Load the Dll and check for errors
hinstDll = LoadLibrary(L"usagelog.dll");
if( hinstDll==NULL ) {
        MessageBox(0, L"Loading of DLL failed", L"Failure", MB_OK);
}

Then, to start the logging InitUsagelog() needs to be called:

void (__cdecl *InitUsagelogPtr)(void) = NULL; // Function pointer with the signature of InitUsagelog

// Obtain a function pointer to InitUsagelog() from the Dll and check for errors
InitUsagelogPtr = (void (*)(void)) GetProcAddress(hinstDll, "InitUsagelog");
if( InitUsagelogPtr==NULL ) {
        MessageBox(0, L"Loading of InitFuncPtr failed", L"Failure", MB_OK);
}
// Start the logging
if( InitUsagelogPtr!=NULL ) {
        InitUsagelogPtr();
}

To stop the logging, ReleaseUsagelog() needs to be called:

void (*ReleaseUsagelogPtr)(void) = NULL; // Function pointer with the signature of ReleaseUsagelog()

// Obtain a function pointer to InitUsagelog() from the Dll and check for errors
ReleaseUsagelogPtr = (void (*)(void)) GetProcAddress(hinstDll, "ReleaseUsagelog");
if( ReleaseUsagelogPtr==NULL ) {
        MessageBox(0, L"Loading of ReleaseFuncPtr failed", L"Failure", MB_OK);
}

Finally, if no more logging is required, the Dll may be unloaded:

FreeLibrary(hinstDll);

Additional Features

  • Unicode logging
    • encoding depends on the compiler, UTF-16LE with Visual Studio 2008
  • Base64 encoding of log to support writing ASCII while using Unicode internally
Last modified 12 years ago Last modified on 10/02/12 13:03:42