Introduction
When I went to update
Part 4 of this article, I thought all I had to do was fix up some VS2008 compiler problems.
I soon discovered there were many other things I wanted to fix or improve, so
I decided to leave
Part 4
as it was with Visual Studio 6, and start a fresh article and code base with
VS2008.
Again I want to thank Bruce Dawson, Mike Carruth, and Grant McDorman, on whose
work I was able to build XCrashReport.
Changes in XCrashReport v1.4
 |
All code and projects converted to VS2008
|
 |
All projects now have ANSI and Unicode builds
|
 |
XCrashHandler now supports 64-bits. Major parts of the exception handler
were rewritten to accommodate 64-bit registers, and all the inline asm code was replaced
with
compiler intrinsics,
since Visual Studio does not allow inline asm code in x64 builds.
The XCrashHandlerTest demo has both x86 and x64 builds.
|
 |
You now can optionally choose to send screenshots of each monitor on your user's
system. The user is shown these screenshot files
in the XCrashReport application, and can choose whether to send them or not.
Included in v1.4 is XBmpViewer, which will display the screenshot when the
user double-clicks a screenshot file name.
|
 |
All files generated by XCrashHandler are now put in the user's documents
folder (CSIDL_PERSONAL ) to avoid UAC interaction.
|
 |
The email send-to address and name are now read from
XCrashReport.ini, which is
loaded from the user's documents folder (CSIDL_PERSONAL ),
again because of UAC.
|
 |
Another crash test was added to the demo app, to generate a crash from a
worker thread.
|
 |
It is no longer necessary to include ExceptionAttacher.cpp.
XCrashHandler now automatically calls
SetUnhandledExceptionFilter()
to install its exception handler. This is done by using
init_seg(lib),
so that XCrashHandler is initialized before your app starts.
|
 |
File paths in the [FilesToAdd] section of
XCrashReport.ini
may now incorporate environment variables.
|
 |
The computer and domain names are now included in ERRORLOG.TXT.
|
 |
A dump of the environment variables is now included in ERRORLOG.TXT.
|
A Note About CSIDL_PERSONAL
On Windows XP, this folder is named
C:\Documents and Settings\<user>\My Documents.
On Windows 7, this folder is named
C:\Users\<user>\Documents.
MSDN has a
complete list of CSIDL values
and their recommended uses. Unfortunately many online articles contain the bad advice to switch to
KNOWNFOLDERID
values. If you follow this advice,
your code
will not work on pre-Vista systems. The code for this article uses only
CSIDL_PERSONAL
when reading or writing files.
Demo App: XCrashReportTest.exe
Here is the demo app:
If running under a debugger, the exception 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).
XCrashReportTest.exe includes the XCrashHandler exception handler,
which catches the exception, gathers system information, and writes ERRORLOG.TXT,
the minidump file CRASH.DMP, and the screenshot image files (one for each monitor).
Next Stop: XCrashReport.exe
As with the previous version, I try to do as little as possible in the exception handler,
which is running in the process space of the crashed app.
When the handler completes, it attempts to run XCrashReport.exe. Of course,
the assumption is that XCrashReport.exe is in the same folder as the exe
of the crashed app.
When XCrashReport.exe starts, it immediately exports any registry keys you have specified,
and then shows this dialog:
At this point your user will be offered the opportunity to inspect the files that will be zipped
and sent via email:
Double-clicking any file in the list will display the file in the view window.
Text files will be shown as text, and binary files will be shown in hex.
Screenshot bmp files will be displayed in the standalone XBmpViewer app.
Notice the two .bmp screenshot files. Although they are 4.5 MB each,
they will be compressed by over 90% when zipped.
You may want to send other files that are associated with your app. In the above
screenshot, you can see files mobydick_ansi.txt, etc. These files and
other settings are specified in XCrashReport.ini.
Your user can now choose whether to send the files he has approved, or
just cancel.
Back at the Ranch
You are relaxing with the latest CodeProject Newsletter when something
hits your inbox. A crash dump for the company's new product! What to do?!?!
Fortunately you don't need to use WinDbg anymore.
Extract CRASH.DMP from the zip file to the folder where
you saved the app's .pdb file (you did save it, didn't you?),
double-click the CRASH.DMP file
and VS2008 starts up. Click Start Debugging (F5) and you will see the
unhandled exception message box:
Click the Break button, and you will be
taken to the source line that caused the exception:
VS2008 is not the only tool that you can use.
WinDbg has even
more powerful capabilities. You can read about it in
Part 3.
Download it
here.
How Does It Work?
This all works because VS2008 uses the app's .pdb file. To generate
a .pdb file for release builds, go to
Project | Properties | C/C++ | Debug Information Format
and select
Program Database (/Zi):
Every time you create a new release build,
you should archive both the .pdb and .exe files.
The .pdb file is not something you want to send outside your company, because
it could be used to reverse-engineer your app.
XCrashReport Architecture
I have stepped through what happens when your app crashes and the
XCrashHandler
exception handler is invoked, and what the
XCrashReport GUI app looks like.
This diagram shows how all the pieces fit together:
"Other files" include any additional files you have specified in
XCrashReport.ini.
Verified Platforms
XCrashReport has been verified to work correctly (with
no UAC interaction)
on the following platforms:
OS |
Version |
Service Pack |
Notes |
2000 |
Professional |
SP4 |
Requires dbghelp.dll |
XP 32-bit |
Professional |
SP2 |
|
Vista 32-bit |
Ultimate |
|
Standard (restricted) user |
Windows 7 32-bit |
Professional |
|
Standard (restricted) user |
Windows 7 64-bit |
Professional |
|
Standard (restricted) user |
Windows 2000 was the only OS that was missing dbghelp.dll;
on all other systems, XCrashReport worked fine using the
dbghelp.dll already on the system.
If you have verified that XCrashReport works on other platforms/service packs,
please send me email so I can add to list.
How To Use
Step 1: Include XCrashHandler Files in your project
All the files you need to include are in the
XCrashHandler folder
in the download:
- CrashOptions.h
- MiniVersion.cpp
- MiniVersion.h
- Screenshot.cpp
- Screenshot.h
- XCrashHandler.cpp
- XCrashHandler.h
- XGetWinVer.cpp
- XGetWinVer.h
All of the
.cpp files above should be set to
Not Using Precompiled Headers.
CrashOptions.h includes all the defines previously contained in
CrashFileNames.h,
EmailDefines.h,
IniDefines.h, and
RegistryDefines.h.
To assist in development, in debug builds
XCrashReport will simulate receiving the string
"XCrashReportTest.exe" on the command line. The release build of
XCrashReport expects to find an argument on the
command line. If the argument is missing, then XCrashReport
exits immediately.
Aside from editing two files (see below), that's all you have to do.
There is no more "ExceptionAttacher" as in the previous version. Setting up the
exception handler is now done automatically via static object
g_XCrashHandler
in XCrashHandler.cpp. The
init_seg(lib)
initializes this static as if it
was a library, which means it's set up before your app is initialized.
You can simply include XCrashHandler files in any project, customize
XCrashReport.ini and CrashOptions.h, and you're good to go.
Step 2: Edit XCrashReport.ini
Edit
XCrashReport.ini to customize registry, files, and email settings.
Note that trying to access a registry key outside of HKCU may involve
interaction with UAC.
;
; sample XCrashReport.ini file
;
[RegistryToAdd]
;RegistryNNN=registry path,description
Registry001=HKCU\Software\CodeProject\XCrashReportTestRU86,Main reg key
Registry002=HKCU\Software\CodeProject\XCrashReportTestRU86\Program,another reg key
[FilesToAdd]
;FileNNN=file path,description,type,text flag
File001=mobydick_ansi.txt,moby dick ansi,Text Document,1
File002=d:\temp\mobydick_unicode_with_bom.txt,moby dick unicode+bom,Text Document,1
File003=d:\temp\mobydick_unicode_no_bom.txt,moby dick unicode,Text Document,1
File004=%TEMP%\mydata.txt,my important data,Text Document,1
[Email]
SendToName=BozoSoft Software Support
SendToAddress=support@bozosoft.com
As the
FILE004 entry shows, you can use environment variables
in the
[FilesToAdd] section. You must ensure that the resulting path
is properly formed - i.e., that the backslash '\' character is added
if necessary.
XCrashReport.ini is not written to by XCrashHandler or
XCrashReport.
Step 3: Edit XCrashOptions.h
Edit
XCrashOptions.h to customize program options.
///////////////////////////////////////////////////////////////////////////////
// SCREENSHOT
///////////////////////////////////////////////////////////////////////////////
// comment out this line if you don't want screenshots
#define XCRASHREPORT_WRITE_SCREENSHOT
// Up to 16 screenshots will be generated, one for each monitor on the user's
// system. You can reduce this to any number below 16.
#define XCRASHREPORT_SCREENSHOT_MAX_MONITORS 16
#define XCRASHREPORT_SCREENSHOT_RESOLUTION_LOW 1
#define XCRASHREPORT_SCREENSHOT_RESOLUTION_MEDIUM 2
#define XCRASHREPORT_SCREENSHOT_RESOLUTION_HIGH 3
// This controls how big the screenshot files are. The files are BMP files,
// and so will be compressed substantially when zipped.
#define XCRASHREPORT_SCREENSHOT_RESOLUTION XCRASHREPORT_SCREENSHOT_RESOLUTION_HIGH
///////////////////////////////////////////////////////////////////////////////
// MINIDUMP
///////////////////////////////////////////////////////////////////////////////
// comment out this line if you don't want minidumps
#define XCRASHREPORT_WRITE_MINIDUMP
///////////////////////////////////////////////////////////////////////////////
// FILE NAMES
///////////////////////////////////////////////////////////////////////////////
#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_BMP_FILE _T("XCRASHREPORT_Mon")
#ifdef _UNICODE
#define XCRASHREPORT_CRASH_REPORT_APP _T("XCrashReportRU.exe")
#define XCRASHREPORT_BMPVIEWER_APP _T("XBmpViewerRU.exe")
#else
#define XCRASHREPORT_CRASH_REPORT_APP _T("XCrashReportRA.exe")
#define XCRASHREPORT_BMPVIEWER_APP _T("XBmpViewerRA.exe")
#endif
///////////////////////////////////////////////////////////////////////////////
// INI FILE
///////////////////////////////////////////////////////////////////////////////
#define INI_FILE_NAME _T("XCrashReport.ini")
#define INI_EMAIL_SECTION _T("Email")
#define INI_EMAIL_SEND_TO_NAME _T("SendToName")
#define INI_EMAIL_SEND_TO_ADDRESS _T("SendToAddress")
#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
///////////////////////////////////////////////////////////////////////////////
// EMAIL - name and address are specified in INI file
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// REGISTRY - keys are specified in INI file
///////////////////////////////////////////////////////////////////////////////
// comment out this line if you don't want to export registry keys
#define XCRASHREPORT_DUMP_REGISTRY
Step 4: Compile and Prepare Installation Package
After compiling your app to include the
XCrashHandler files,
you need to include the following files in the installation package:
Building with Visual Studio 2005
kinar was kind enough to provide the following information
for building
XCrashReport with VS2005:
- Edit the .sln files
and replace
Microsoft Visual Studio Solution File, Format Version 10.00
# Visual Studio 2008
with
Microsoft Visual Studio Solution File, Format Version 9.00
# Visual Studio 2005
- Edit the .vcproj files
and replace
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9.00"
with
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
Known Limitations
Links
This is a cumulative list of all the links that have been mentioned
in Parts 1 - 5.
Revision History
Version 1.4.1 - 2010 July 2
- Fixed problem with file path display in XCrashReportTest,
reported by kinar
Version 1.4 - 2010 June 30
- All code and projects converted to VS2008
- All projects now have ANSI and Unicode builds
- Now supports 64-bits
- Optional screenshots
- All files now put in the user's documents folder
(
CSIDL_PERSONAL
) to avoid UAC interaction
- XCrashReport.ini now read from user's documents folder
(
CSIDL_PERSONAL
) to avoid UAC interaction
- The email send-to address and name are now read from XCrashReport.ini
- Crash test was added to the demo app to generate a crash from a
worker thread
- It is no longer necessary to include ExceptionAttacher.cpp.
XCrashHandler now automatically calls
SetUnhandledExceptionFilter()
to install its exception handler
- File paths in the [FilesToAdd] section of
XCrashReport.ini
may now incorporate environment variables
- The computer and domain names are now included in ERRORLOG.TXT
- A dump of environment variables is now included in ERRORLOG.TXT
Version 1.1 - 2003 October 19
Usage
This software is released under the
Code Project Open License (CPOL).
You are free to use this software 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.