If you are
a software vendor, you might have faced with the problem of fixing the crashes
(critical errors, exceptions) of your app on user’s machine located on the
other side of the globe. For example, assume a user writes you an email where he describes
the error in your software. Of course you are interested in making the user happy, so you start
asking him to provide more information, to give a screenshot or an error message.
The problem is that user has no technical knowledge and usually can’t provide
you with as many details as you need to reproduce the crash. Typical users
would just give up using your application if it crashes frequently and start using your
comptetitor’s software (sounds sad). <o:p>
can be done to collect technical information about errors more easily? The
answer is distributing a crash reporting
library with your software, which would collect all required information about
the problem and send the information to you (user just would need to provide
his consent by pressing the “Send report” button). The technical information the crash reporting library would collect for you includes the following: the crash minidump file containing the call stack at the moment of crash helping you to see the line of code where exception had happened, screenshot of the desktop at the moment of crash helping you to see what button user had clicked and helping you reproduce the problem.
In this tutorial, I will show how to integrate one such an Open-source crash reporting library
called CrashRpt with your application. You may be already interested if CrashRpt works with your application or not. If your application is written in C/C++ using Visual C++ .NET 2003, 2005, 2008, 2010 or Visual C++ Express, and if your app is WinAPI/ATL/WTL/MFC-based, the answer is yes. CrashRpt supports Win32 and Win64 processor architectures. It works in Windows 2000, XP, Vista and Windows 7.
For the demonstration, I use an MFC application, because I figured out that using CrashRpt with MFC may cause some confusion. For example, one problem MFC users experience is determining the right place in the code where to initialize the crash reporting library.
This article is aimed to be a beginner’s tutorial. The source code and binary files I use for demonstration are attached to the article. The code of CrashRpt library (v.1.3.0) is also attached, but it is recommended that you download the latest version from the external site.
In conclusion of the tutorial, I give some links for an interested reader who may want to become familiar with more advanced topics, like using CrashRpt in a multi-threaded application, sending error report over the Internet to an HTTP server, postprocessing error reports using a command line tool and so on.
Note: You may also refer to the Add Crash Reporting to Your Applications with the CrashRpt Library article by Mike Carruth, which is a little obsolete, but contains many interesting details of CrashRpt usage.
Integrating CrashRpt with your Application
First of all, we will create a simple document-view MFC application which always crashes when saving the document through File->Save menu.
Simple MFC Application
In this tutorial, we will create a very simple MFC application from scratch in Visual Studio 2005. To do this, we open Visual Studio window and then open menu File and select the New->Project… from the menu.
In the appeared New Project dialog, we choose MFC Application from the templates list and enter the SimpleApp name into the Name field and press the OK button. Then, when the MFC Application Wizard – SimpleApp dialog appears, we just click the Finish button to generate project files for the new SimpleApp application.
Now, we will add a code that would crash the application. Of course, we can insert such a bad code into any place of our application (as it happens in real life), but to make things simple, we will make our app crash when saving the document. To do so, in the SimpleAppDoc.cpp file, modify the
CSimpleAppDoc::Serialize() method the following way:
void CSimpleAppDoc::Serialize(CArchive& ar)
int* p = NULL;
*p = 13;
The method above contains
NULL pointer assignment, which when executed, will rise an access violation exception.
Note: If you are interested to know what else code may crash your application, you can refer to the Making Your C++ Code Robust article.
Finally, we press F5 to compile and run the created application. The application window should appear (see the figure below).
Figure 1 – SimpleApp MFC Application Window
To see what happens on crash, in the SimpleApp window, we open menu File and choose Save. We enter some file name and press the Save button. The application will terminate with an error message.
You can find the application we’ve just created attached to this tutorial (SimpleApp.zip archive).
Now, we will install the CrashRpt library. In this demo, we will use the latest version of CrashRpt available at the time of writing this tutorial - v.1.3.0.
First, we should get the CrashRpt library source code. You can download the latest CrashRpt distribution archive from here. The archive uses 7z format, so we can unpack the archive with the 7zip tool. We unpack it to some folder, for example to C:\CrashRpt.
Note: The CrashRpt v.1.3.0 source code is also attached to this article, but it is recommended that you get the latest version from CrashRpt project website.
Let’s look inside the CrashRpt folder. It contains several subfolders and files.
The bin subfolder contains compiled CrashRpt binaries (CrashRpt1300.dll, CrashSender1300.exe and so on). CrashRpt consists of two core modules: CrashRpt1300.dll and CrashSender1300.exe. CrashRpt1300.dll contains functionality for intercepting exceptions in a client software. CrashSender1300.exe contains functionality for compressing and sending error reports to the software's support team. CrashRpt is separated into these modules to be able to close the application which have crashed and to continue sending the error report in CrashSender1300.exe in background.
The include and lib subfolders contain header files and import library files. We will need these files later when compiling and linking the SimpleApp application.
The lang_files subfolder contains language INI files named like crashrpt_lang_XX.ini, where XX is a language abbreviation. The INI files contain localized strings for CrashRpt dialogs, so you can localize it to your favourite language.
The top-level folder contains file CrashRpt_vs2010.sln, which is the CrashRpt solution file for Visual Studio 2010. If you use Visual Studio 2010, you can double-click this file and refer to the Compiling CrashRpt section below. But in this tutorial, I will show how to generate CrashRpt solution file for an older version, Visual Studio 2005.
Generating CrashRpt Project Files in CMake
We will use CMake cross-platform make system to generate CrashRpt solution file very easily. If you don’t have CMake, download its installer from here and install CMake on your computer. I use the latest version at the moment, CMake 2.8.7.
Next, we will run the CMake-GUI wizard by opening menu Start and choosing CMake 2.8->CMake (cmake-gui). The CMake dialog should appear. In the dialog, we need to provide the path to folder where we've unpacked the CrashRpt archive (in our case, C:\CrashRpt). Enter this path into the “Where is the source code:” and “Where to build the binaries:” fields as shown in the figure below and press the Configure button.
Figure 2 – Generating CrashRpt Project Files for Visual Studio 2005 with CMake
The generator selection dialog appears where we need to select Visual Studio 8 2005 from the drop-down list and press the Finish button. Then press the Generate button. If everything is OK, you should see the “Generating done” message. Now, go to C:\CrashRpt folder and you should be able to see the CrashRpt.sln file. Double-click the file to open it in Visual Studio.
Compiling CrashRpt yourself is strongly recommended if you want CrashRpt to handle exceptions that may occur in C run-time (CRT) libraries. CrashRpt distribution archive already contains compiled CrashRpt binaries, but it is not recommended to use them with your software, because your software may use different C run-time DLLs, and CrashRpt won't be able to intercept exceptions in your C run-time libraries.
Compiling CrashRpt is very straightforward – you just need to select Release configuration and press F7. If everything OK, you should be able to find CrashRpt binaries in the bin subfolder.
Using CrashRpt API
Now, we add CrashRpt include and lib folders to the Visual Studio search path to let Visual C++ compiler and linker know about the location of CrashRpt include and lib files.We accomplish this by opening menu Tools-> Options in Visual Studio window. Then in appeared dialog, we select Projects and Solutions->VC++ Directories. Finally, in the Show directories for combo box, select Include files, then add the path to C:\CrashRpt\include directory to the list. In the Show directories for combo box, select Library files, then add the path to C:\CrashRpt\lib directory to the list.
To be able to use CrashRpt API functions, we include
CrashRpt.h header file in the beginning of the SimpleApp.cpp file.
When application starts, we need to initialize CrashRpt. We do this by inserting some CrashRpt functions into the SimpleApp application code. But we need insert them into the right place which is called
CWinApp::Run() method. We insert the following code into the SimpleApp.cpp file:
We have overridden the
CWinApp::Run() method. The
CWinApp::Run() method is called when an MFC application starts, so this is the right place to initialize CrashRpt.
Next, we initialize CrashRpt by calling the
crInstall() function and pass it the configuration parameters through
CR_INSTALL_INFO structure. Below our
Run() method with inserted CrashRpt API functions is presented:
memset(&info, 0, sizeof(CR_INSTALL_INFO));
info.cb = sizeof(CR_INSTALL_INFO);
info.pszAppName = _T("SimpleAp"); info.pszAppVersion = _T("1.0.0"); info.pszUrl = _T("http://someserver.com/crashrpt.php");
info.dwFlags |= CR_INST_ALL_POSSIBLE_HANDLERS;
info.dwFlags |= CR_INST_HTTP_BINARY_ENCODING;
info.pszPrivacyPolicyURL = _T("http://someserver.com/privacy.html");
int nResult = crInstall(&info);
MessageBox(NULL, buff, _T("crInstall error"), MB_OK);
We also call the
crAddScreenshot2() function. We tell it to add a screenshot of the application window to the error report. The screenshot image will use compressed JPEG format with 95% quality to reduce image size.
Finally, we call
crUninstall() function before exiting from
Run() method to deinitialize the library.
Ok, now we need to add CrashRpt1300.lib to the list of input libraries for the project. In the Solution Explorer window, right-click the project node and choose Properties item from the context menu. Then open Configuration Properties->Linker->Input->Additional Dependencies and then add CrashRpt1300.lib to the list of libraries.
Select the Release build configuration and press F7 to build the project.
Running the Application
We are almost ready to run the SimpleApp application. But before we do this, we should copy the following files from C:\CrashRpt\bin folder to the folder where the application executable file is located:
These files are required for CrashRpt to work properly. The files CrashRpt1300.dll and CrashSender1300.exe are core CrashRpt modules. The dbghelp.dll file is the Microsoft Debug Help Library, CrashRpt depends on this module. The crashrpt_lang.ini file contains localized strings for CrashRpt dialogs, so you can localize it to your favourite language.
When files have been copied, run the SimpleApp.exe file. In the appeared SimpleApp window, open menu File and choose Save from the menu. Enter some file name and press the Save button. When the access violation happens, you should see a nice-looking CrashRpt Error Report window as shown it the figure below.
Figure 3 – Error Report Window
Let's review what is displayed on the Error Report window.
We see that the report contains 165 KB of data. This is rather small and acceptable for transferring over the Internet. CrashRpt library can transfer the data as a request to HTTP server or as an E-mail message with attachments. The recipient recieves the error report as a compressed ZIP archive containing several files.
To send the generated error report to the HTTP server you have specified, press the Send report button. If you do not want to send the report, press Close the program button. Note, that in this tutorial I do not show how to send error report over the Internet, you should provide a real recipient's address to send error reports as E-mail and/or configure a server-side script to send error reports over HTTP connection.
By clicking the What does this report contain? link, you may review the contents of the generated error report. In the figure below, you can see that the report we’ve generated contains three files: crashdump.dmp (crash minidump), crashrpt.xml (crash description XML) and screenshot0.jpg (desktop screenshot).
Figure 4 – Error Report Details Dialog
The crash minidump file can be used to debug the crash. You can double-click the crashdump.dmp file to open it in Visual Studio and see the line of the code where exception had happened.
There is also a nice-looking HEX, text or image preview for each file in the error report. The figure below displays a preview of the JPEG screenshot of our app's window that may be useful to reproduce the crash.
Figure 5 - Desktop Screenshot Preview
Crash reporting allows you to automatically collect technical information about errors in your software to later postprocess the info on developer’s side. In this tutorial, I’ve demonstrated how to integrate the CrashRpt crash reporting library into an MFC application.
This tutorial is very simple, and intended for beginners. In this tutorial, I haven’t covered the advanced topics of installing CrashRpt into a multi-threaded application, adding custom files to the error report, sending error reports over the Internet as an E-mail message or as a request to an HTTP server and analyzing arriving error reports using a command-line tool. An interested reader may find more information on these and other topics in CrashRpt online documentation.
For those, who want to better understand exception handling in Visual C++, I would recommend the article Effective Exception Handling in Visual C++, where I describe in details how CrashRpt catches exceptions in a C++ program. If you are interested to learn how the HEX file preview shown on the figure above works, you can refer to the article FilePreviewCtrl – Preview Files in Text, HEX and Image Format.
January 1st 2012 - Initial release