Introduction
If you are a software vendor, you might have faced 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 competitor’s software (sounds sad).
So, what 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 in the following way:
void CSimpleAppDoc::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
int* p = NULL;
*p = 13;
}
else
{
}
}
The method above contains NULL
pointer assignment, which when executed, will raise an access violation exception.
Note: If you are interested to know what other 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.
Downloading CrashRpt
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 the 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
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 is 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 at the beginning of the SimpleApp.cpp file.
#include <CrashRpt.h>
When application starts, we need to initialize CrashRpt
. We do this by inserting some CrashRpt
functions into the SimpleApp
application code. But we need to insert them into the right place which is called CWinApp::Run()
method. We insert the following code into the SimpleApp.cpp file:
int CSimpleAppApp::Run()
{
BOOL bRun;
BOOL bExit=FALSE;
while(!bExit)
{
bRun= CWinApp::Run();
bExit=TRUE;
}
return bRun;
}
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:
int CSimpleAppApp::Run()
{
CR_INSTALL_INFO info;
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);
if(nResult!=0)
{
TCHAR buff[256];
crGetLastErrorMsg(buff, 256);
MessageBox(NULL, buff, _T("crInstall error"), MB_OK);
return 1;
}
crAddScreenshot2(CR_AS_MAIN_WINDOW|CR_AS_USE_JPEG_FORMAT, 95);
BOOL bRun;
BOOL bExit=FALSE;
while(!bExit)
{
bRun= CWinApp::Run();
bExit=TRUE;
}
crUninstall();
return bRun;
}
In the code above, we install all available exception handlers into the application. We specify the application name and version, because this information is needed to identify the application that sends the report. We provide an URL for transferring the error report to an HTTP server using the binary transfer encoding. We also provide a privacy policy URL.
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:
CrashRpt1300.dll
CrashSender1300.exe
dbghelp.dll
crashrpt_lang.ini
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 string
s 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 in the figure below.
Figure 3 – Error Report Window
Let's review what is displayed on the Error Report window.
The Privacy Policy link allows to see the privacy policy we use when collecting data from user. The privacy policy typically states we use the data to improve the software and we do not sell or otherwise transfer the data to third parties.
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 receives 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
Conclusion
Crash reporting allows you to automatically collect technical information about errors in your software to later postprocess the information on the 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 detail how CrashRpt
catches exceptions in a C++ program. If you are interested to learn how the HEX file preview shown in the figure above works, you can refer to the article FilePreviewCtrl – Preview Files in Text, HEX and Image Format.
History
- 1st January, 2012 - Initial release