Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

XCrashReport : Exception Handling and Crash Reporting - Part 4

0.00/5 (No votes)
19 Oct 2003 6  
Add basic exception handling and crash reporting to your application
Update: Part 5 is now available with support for VS2008 and 64-bits.

Introduction

In Part 3, I said that I thought that article was the last one about exception handling and crash reporting. But I had ignored one important question: how are you going to get your customers to send you the crash dumps and error logs? This really worried me, until I saw the wonderful solution here that Mike Carruth came up with. His idea was to replace the standard crash dialog that looks like this:

screenshot

with one that prompts you to email the crash files to the software vendor. The really brilliant thing about Carruth's approach is that it allows the customer to review in detail what is being sent, and to choose what files are sent.

End of the Grail?

OK, now you are thinking, "Good! We can just use what Carruth has done." For me, I had some problems with his implementation.
Problem #1: It uses WTL. Not a big deal, but it is one more thing to configure and deal with, and I am very reluctant to tweak some of the older MFC apps I maintain.

Problem #2: The replacement crash dialog runs in the same process as the crashed app. This is a big concern, because you really want to do as little as possible in the exception handler, and GUI dialogs cross the line.

Problem #3: It uses zlib.dll to zip the crash files. This is one more file to add to the installation, and I get twitchy when I think about dealing with that.

Problem #4: There is extra stuff in the implementation that was not essential for me. For example, output of the crash info to XML using Microsoft's MSXML. While I think this is a very interesting use of XML, I wanted to keep the exception handler (and the code it pulls in) to a very small footprint.

Preparation

My first requirement was to run the crash dialog as a separate app, to avoid any problems with the app that just crashed; my second was to use only MFC.

Running as a separate app was no problem. Since the exception handler was doing all the work of creating the crash files, all the new crash dialog app had to do was to find them, zip them, and email the zip. To make the crash dialog app (I am calling it XCrashReport from now on) as independent as possible, the last thing the exception handler does is to invoke XCrashReport with the name of the crashed app on the command line. By default, I decided that the crashed app, the crash files, and XCrashReport must all reside in the same directory. This design decision could easily be changed, but so far it seems to work well for my needs.

Next came the MFC stuff. I knew there would be two dialogs - the main window, and then a dialog that showed the details of the files being sent, and allowed the user to select which ones to send. For the last, I saw that my XListCtrl would be perfect, using checkboxes to allow selection. I could also use my new XHyperLink and XColorStatic controls. And I could use my XZip code to zip up the crash files.

I would like to thank Grant McDorman for his enhancements to Mike Carruth's code, which you can find here. In particular, McDorman added the ability to dump a section of the registry to a text file, which can then be added to the error report.

Theory Into Practice

After modifying the ExceptionHandler.cpp code once more, to start up XCrashReport.exe, the new crash report dialog will be displayed if it is found in the same directory as the crashed app:

screenshot

The user can then click on the click here link to review the contents of the error report before it is sent. This shows the ERRORLOG.TXT file being displayed:

screenshot

This shows the CRASH.DMP file being displayed:

screenshot

This shows the REGISTRY.TXT file being displayed:

screenshot

Summary

This is now a complete solution to catching application exceptions, generating a detailed error report, and prompting the user to send the report to you. Thanks to the work of Bruce Dawson, Mike Carruth, and Grant McDorman, XCrashReport offers a minimal footprint along with a user-friendly crash dialog.

Implementation Notes

Dismissing the Exception

At the end of the RecordExceptionInfo() function in ExceptionHandler.cpp, there is code that will detect whether the app is running under a debugger, and dismiss the exception accordingly:
  • If running under a debugger, the handler returns EXCEPTION_CONTINUE_SEARCH. This tells Win32 that this handler didn't actually handle the exception, so that things will proceed as per normal, and the debugger will catch the exception.
  • If not running under a debugger, the handler attempts to run XCrashReport.exe. If this is successful, the handler returns EXCEPTION_EXECUTE_HANDLER, which suppresses the standard crash dialog. If not successful, the handler returns EXCEPTION_CONTINUE_SEARCH, which again lets things proceed as per normal (the standard crash dialog will pop up).

Customizable Files

There are some files that you may want to customize for your own application and company:
  • CrashFileNames.h - this contains the names of the error output files:
    #define XCRASHREPORT_MINI_DUMP_FILE     _T("CRASH.DMP")
    #define XCRASHREPORT_ERROR_LOG_FILE     _T("ERRORLOG.TXT")
    #define XCRASHREPORT_REGISTRY_DUMP_FILE _T("REGISTRY.TXT")
    #define XCRASHREPORT_CRASH_REPORT_APP   _T("XCrashReport.exe")
  • EmailDefines.h - this contains strings for email headers:
    #define XCRASHREPORT_SEND_TO_NAME      _T("Software Support")
    #define XCRASHREPORT_SEND_TO_ADDRESS   _T("support@softwarevendor.com")
  • IniDefines.h - this contains defines for the XCrashReport.ini file:
    #define INI_FILE_NAME      _T("XCrashReport.ini")
    #define INI_FILE_SECTION   _T("FilesToAdd")
    #define INI_FILE_TEMPLATE  _T("File%03d")
    #define INI_REG_SECTION    _T("RegistryToAdd")
    #define INI_REG_TEMPLATE   _T("Registry%03d")
    #define MAX_INI_ITEMS      999
  • RegistryDefines.h - this contains the define which specifies whether any registry sections will be dumped, and also the default registry section:
    #define XCRASHREPORT_DUMP_REGISTRY
    #define XCRASHREPORT_REGISTRY_KEY \
                         _T("HKCU\\Software\\CodeProject\\XCrashReportTest")
  • XCrashReport.ini - this ini file is read by XCrashReport.exe and contains the registry sections to be dumped to file and the files that are to be included in the error report zip. It is not necessary to include the registry files in the [FilesToAdd] section.
    [RegistryToAdd]
    ;Registry001=HKCU\Software\CodeProject\XCrashReportTest,Main reg key
    ;Registry002=HKCU\Software\CodeProject\XCrashReportTest\Program,new reg key
    
    [FilesToAdd]
    ;File001=CRASH.DMP,Crash Dump,DMP File
    ;File002=ERRORLOG.TXT,Crash log,Text Document

Using XCrashReport in Debug Mode

To simplify working on XCrashReport, in debug builds XCrashReport will simulate receiving the string "XCrashReportTest.exe" on the command line.

How To Use

  1. Set up your release build to generate debug symbols (pdb)
  2. Include these files in your project:
  3. Recompile the entire project.

    • In your VC++ project, go to Project | Settings. Make sure the Release configuration is selected in the Settings For combobox on the left. Go to the C/C++ tab, select the General category, and select Program Database in the Debug Info combobox. This tells the compiler to generate debug information.

      screenshot

    • Go to the Link tab and check Generate debug info. This tells the linker to collate debug information into .pdb files. The linker also puts the name of the .pdb file in the executable, so the debugger can find it.
    • On the same Link tab, enter /OPT:REF at the end of the Project Options list. This tells the linker to eliminate functions and/or data that are never referenced. This is the usually the default for release builds, but it gets turned off when you tell the linker to generate debug information. Failing to specify /OPT:REF will cause your executables and DLLs to get 10-20% larger.

      screenshot


    • ExceptionAttacher.cpp
    • ExceptionHandler.cpp - should be set to Not using precompiled headers on the C/C++ tab (Precompiled Headers).
    • ExceptionHandler.h
    • GetWinVer.cpp
    • GetWinVer.h
    • MiniVersion.cpp
    • MiniVersion.h
    • CrashFileNames.h


    Tuck away the exe and pdb files. Do not ship the pdb file to customers - this is both unnecessary and may be helpful to someone wanting to reverse-engineer your program.

A Note on dbghelp.dll

The same download for WinDbg also includes the latest dbghelp.dll. You can download it here. In this download, the redist.txt file states that dbghelp.dll version 6.2.13.1 is redistributable.

The download also includes the latest dbghelp.lib and dbghelp.h.

Known Limitations

  • XCrashReport.exe must reside in the same directory as the application's exe.
  • The Unicode implementation is not complete or tested.
  • The amount of text that can be entered in the user's comments editbox is limited to 64 KB characters.
  • The amount of the file that will be displayed in the Error Report Contents dialog is limited to 64 KB.
  • There must be a default email client installed in order for the error report to be sent by email. This has been tested with Outlook, Outlook Express, and Eudora. If there is no default email client installed, the user is requested to email the zip file, but there is no attempt to configure MAPI or create an email session.

Frequently Asked Questions

  1. Can I use ExceptionHandler.cpp in non-MFC apps?
    Yes! It has been implemented to compile with any Win32 program. See Part 1 for an example.
  2. How can I add some additional files to the error report?
    How can I add some additional registry sections to the error report?
    Use the optional XCrashReport.ini file. (There is a sample XCrashReport.ini file included.) When you specify files in the XCrashReport.ini file, the default files (CRASH.DMP and ERRORLOG.TXT) are not included - you must add them to the XCrashReport.ini file if you want to include them. Also, When you specify registry sections in XCrashReport.ini file, the default registry section (defined by XCRASHREPORT_REGISTRY_KEY) is not included - you must add it to the XCrashReport.ini file if you want to include it. On the target system, the XCrashReport.ini file must be placed in the exe directory.

    Here is an example XCrashReport.ini file:

    ;
    ; sample XCrashReport.ini file
    ;
    [FilesToAdd]
    File001=CRASH.DMP,Crash Dump,DMP File
    File002=ERRORLOG.TXT,Crash log,Text Document
    File003=BozoSoft.db,Database File,DB File
    
    [RegistryToAdd]
    Registry001=HKCU\Software\BozoSoft\BozoApp1,Main reg key
    Registry002=HKCU\Software\BozoSoft\BozoApp2\Program,another reg key
    There is no limit on the size of the file that may be added, although internally only the first 64 KB of the file will be displayed on the Error Report Contents dialog.
  3. When I try to include ExceptionHandler.cpp in my MFC project, I get the compiler error ExceptionHandler.cpp(823) : fatal error C1010: unexpected end of file while looking for precompiled header directive. How can I fix this?
    When using ExceptionHandler in project that uses precompiled headers, you must change C/C++ Precompiled Headers settings to Not using precompiled headers for ExceptionHandler.cpp. Be sure to do this for All Configurations.

    XCrashReport screenshot

  4. I don't need registry dumps. Can I exclude them?
    Yes. Comment out the following line in RegistryDefines.h:
    #define XCRASHREPORT_DUMP_REGISTRY
    This file is included in the XCrashReport.exe project.
  5. I don't need minidumps. Can I exclude them?
    Yes. Comment out the following line at the top of ExceptionHandler.cpp:
    #define XCRASHREPORT_WRITE_MINIDUMP
    This file is one of the files that you include in your app's project.
  6. I want to rename the default error files that are created. How can I do this?
    You can edit the file CrashFileNames.h:
    // CrashFileNames.h  Version 1.0
    //
    // Author:  Hans Dietrich
    //          hdietrich2@hotmail.com
    //
    // This software is released into the public domain.
    // You are free to use it in any way you like, except
    // that you may not sell this source code.
    //
    // This software is provided "as is" with no expressed
    // or implied warranty.  I accept no liability for any
    // damage or loss of business that this software may cause.
    //
    /////////////////////////////////////////////////////////////////////
    
    #ifndef CRASHFILENAMES_H
    #define CRASHFILENAMES_H
    
    #define XCRASHREPORT_MINI_DUMP_FILE     _T("CRASH.DMP")
    #define XCRASHREPORT_ERROR_LOG_FILE     _T("ERRORLOG.TXT")
    #define XCRASHREPORT_REGISTRY_DUMP_FILE _T("REGISTRY.TXT")
    #define XCRASHREPORT_CRASH_REPORT_APP   _T("XCrashReport.exe")
    
    #endif //CRASHFILENAMES_H
    This file is one of the files that you include in your app's project, and it is also used in XCrashReport.exe. Both will need to be recompiled.
  7. How do I change the registry section that gets dumped?
    You can use the XCrashReport.ini file (see above), or you can edit the file RegistryDefines.h:
    // RegistryDefines.h  Version 1.0
    //
    // Author:  Hans Dietrich
    //          hdietrich2@hotmail.com
    //
    // This software is released into the public domain.
    // You are free to use it in any way you like, except
    // that you may not sell this source code.
    //
    // This software is provided "as is" with no expressed
    // or implied warranty.  I accept no liability for any
    // damage or loss of business that this software may cause.
    //
    //////////////////////////////////////////////////////////////////////
    
    #ifndef REGISTRYDEFINES_H
    #define REGISTRYDEFINES_H
    
    #define XCRASHREPORT_DUMP_REGISTRY
    #define XCRASHREPORT_REGISTRY_KEY \
                         _T("HKCU\\Software\\CodeProject\\XCrashReportTest")
    
    #endif //REGISTRYDEFINES_H
    
    
    This file is used in XCrashReport.exe.
  8. Can we use XCrashReport in our (shareware/commercial) app?
    Yes, you can use XCrashReport without charge or license fee. It would be nice to acknowledge my Copyright in your About box or splash screen, but this is up to you.

Links

This is a cumulative list of all the links that have been mentioned in Parts 1 - 4.
Bruce Dawson's article "Release mode debugging with VC++" http://www.cygnus-software.com/papers/release_debugging.html
Bruce Dawson's original exception handler code from Game Developer Magazine ftp://ftp.gdmag.com/pub/src/jan99.zip
Mike Carruth's article "Add Crash Reporting to Your Applications with the CrashRpt Library" http://www.codeproject.com/debug/crash_report.asp
Grant McDorman's enhancements to Mike Carruth's code http://www3.sympatico.ca/grant.mcdorman/
XListCtrl - A custom-draw list control with subitem formatting http://www.codeproject.com/listctrl/xlistctrl.asp
XColorStatic - a colorizing static control http://www.codeproject.com/useritems/XColorStatic.asp
XHyperLink - yet another hyperlink control http://www.codeproject.com/useritems/XHyperLink.asp
XZip and XUnzip - Add zip and/or unzip to your app with no extra .lib or .dll http://www.codeproject.com/cpp/xzipunzip.asp
Microsoft Debugging Tools, including WinDbg and dbghelp.dll http://www.microsoft.com/whdc/ddk/debugging
MiniDumpWriteDump API http://msdn.microsoft.com/library/default.asp?url=/library/en-us/debug/base/minidumpwritedump.asp
Part 1 http://www.codeproject.com/useritems/XCrashReportPt1.asp
Part 2 http://www.codeproject.com/useritems/XCrashReportPt2.asp
Part 3 http://www.codeproject.com/useritems/XCrashReportPt3.asp

Revision History

Version 1.1 - 2003 October 19

  • Initial public release

Usage

This software is released into the public domain. You are free to use it in any way you like, except that you may not sell this source code. If you modify it or extend it, please to consider posting new code here for everyone to share. This software is provided "as is" with no expressed or implied warranty. I accept no liability for any damage or loss of business that this software may cause.

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