Examining bugs in an already shipped executable can be tricky at best as developers most likely cannot be out in the field and debug their faulty programs on site with their development tools.
This dynamic link library for Windows applications eases the problem by providing an exception handler that in case of an crashing program will provide a detailed stack trace of the crash that the end user optionally can email back to the developer.
The source code ofthe library is partly based on examples by Matt Pietrek, Paul DiLascia found in the MSDN as well as the ZLIB compression algorithm by Jean-loup Gailly and Mark Adler. It exposes functions the following functions:
void InitialiseXCPTHandler(LPCTSTR szcompanyname, LPCTSTR szemailaddress, HWND hwnd = NULL);
void StopUsingXCPTHandler();
bool SetDiagnosticXCPTHandler(DWORD diagthrdID);
void UseMyXCPTHandler();
void TagAs3rdPartyModule(LPCTSTR szmodule);
The first two are mandatory whereas the remaining three can be optionally used by the developer depending on circumstances. When calling InitialiseXCPTHandler the library will override whatever exception mechanism was put in place (usually Windows default error message box) with its own exception handling.
When the program then encounters an error XCPTLib will arrest the program and pop up a dialog box with some options before it terminates the program. If debug symbols are available in a program data base (pdb) file with the executable and line numbers have been compiled into the executable code the report generated will tell the user where in the program source the crash occurred; like a simple stack trace. In addition to that, the library will create a crash dump file and compress it with the zlib code. The user has the option of emailing this crash dump file to the developer if the developer has supplied an email address in the call to InitialiseXCPTHandler(). By loading the crash dump file into Visual Studio or WinDbg it is straightforward then to produce the stack trace and examine variables provided the source code and the pdb file matches the executable version that crashed.
Some effort has been made to in order to retain the library as the only exception handler. If the developer is using third party modules in his own code, to which he may not have the source code these could potentially set the exception handling to their own customised handlers. The developer can reset the exception handler to XCPTLib again by calling UseMyXCPTHandler() whenever he suspect that a third-party library may have changed the global exception handler.
In a multithreaded program a bug may not occur in the main thread. There may be situations when this happens where it is desirable that the user still has some control over the program and that it doesn't just stop after encountering a bug in a particular thread, say one that was started by a thrid-party library. To that end the developer can call functions SetDiagnosticXCPTHandler(DWORD diagthrdID) where diagthrdID is the ID for a thread that should be stopped if throws an exception. If this is not the main thread the user should in theory have the option to terminate the program gracefully.
If an exception is thrown by a third-party library the developer can flag this in the simple stack trace as the possible sorce of the exception by calling TagAs3rdPartyModule(LPCTSTR szmodule) where szmodule is the file name of the third-party dll. The simple stack trace will then read "Possible origin of this exception!" when the stack trace happens to go into the third-party dll.
There are two ways in which this llibrary can be invoked. Either by statically linking the library at compile time or by dynamic linking at runtime.
With static linking the library the developer should define the preprocessor macro XCPTLIB_API for his project, #include "XCPTLib.h" and refer the linker to the XCPTLib.lib file.
With dynamic linking the developer should do the following in his projects main file:
---------------------------------------------------------------------------------------------------
// define function pointers for our exceptionhandler library, XCPTLib.dll
typedef void (*INITXCPT)(LPCTSTR szcompanyname, LPCTSTR szemailaddress, HWND hwnd);
typedef void (*STOPXCPT)();
// declare pointers for the exception handler functions
INITXCPT pinitxcpt;
STOPXCPT pstopxcpt;
HINSTANCE hXCPTLib;
void main()
{
INITXCPT pinitxcpt = NULL;
STOPXCPT pstopxcpt = NULL;
HINSTANCE hXCPTLib = NULL;
hXCPTLib = LoadLibrary(TEXT("XCPTLib.dll"));
if (hXCPTLib != NULL)
{
// assign function pointers
pinitxcpt = (INITXCPT) GetProcAddress(hXCPTLib, TEXT("InitialiseXCPTHandler"));
pstopxcpt = (STOPXCPT) GetProcAddress(hXCPTLib, TEXT("StopUsingXCPTHandler"));
if (pinitxcpt) // initialise exception handler library
pinitxcpt("Developer John Doe", "john@doe.org", NULL);
}
// main program goes below
...
MyIngeniousProgram();
...
// end of main program
if (pstopxcpt)
{// if exception handling library XCPTLib.dll was present then uninitialise and release it
pstopxcpt();
FreeLibrary(hXCPTLib);
}
}
-------------------------------------------------------------------------------------------------------
The other three functions in XCPTLib can obviously also be called with dynamic linking but are left out here for clarity.
Note on compilation of the developers project: The developers project should be compiled to generate debug symbols in a pdb file (compiler option /Zi or /Z7 in Visual Studio 2005). If your project is not open source then the resulting pdb file should not be shipped with the executable as there's a potential risk of decompile the executable with the help of the pdb file. Instead the pdb file together with the source code must be stored in a version control system so that a crash dump file sent be some user using the versioned executable can now be debugged.