将崩溃报告集成到您的应用程序中 - 初学者教程






4.91/5 (29投票s)
本文介绍了如何将 CrashRpt 错误报告库与 MFC 应用程序一起使用
引言
如果您是软件供应商,您可能遇到过在用户机器上修复应用程序崩溃(临界错误、异常)的问题,而用户的机器可能远在天涯海角。例如,假设一个用户给您写了一封电子邮件,描述了您软件中的某个错误。当然,您希望让用户满意,所以您会开始要求他提供更多信息,提供屏幕截图或错误消息。问题是,用户没有技术知识,通常无法为您提供重现崩溃所需的大量详细信息。典型的用户会因为应用程序频繁崩溃而放弃使用,转而使用竞争对手的软件(听起来很伤感)。
那么,为了更轻松地收集技术错误信息,可以做什么呢?答案是:将崩溃报告库分发到您的软件中,该库可以收集有关问题的所有必需信息并将其发送给您(用户只需通过按下“发送报告”按钮来提供同意)。崩溃报告库会为您收集的技术信息包括:崩溃时的最小转储文件,其中包含崩溃时的调用堆栈,帮助您看到异常发生的代码行;崩溃时的桌面屏幕截图,帮助您看到用户点击了哪个按钮,并帮助您重现问题。
在本教程中,我将向您展示如何将一个名为 CrashRpt 的开源崩溃报告库集成到您的应用程序中。您可能已经对 CrashRpt
是否适用于您的应用程序感到好奇。如果您的应用程序是用 C/C++ 编写的,使用 Visual C++ .NET 2003、2005、2008、2010 或 Visual C++ Express,并且您的应用程序是基于 WinAPI/ATL/WTL/MFC 的,那么答案是肯定的。CrashRpt
支持 Win32 和 Win64 处理器架构。它可以在 Windows 2000、XP、Vista 和 Windows 7 中运行。
为了演示,我使用了一个 MFC 应用程序,因为我发现将 CrashRpt
与 MFC 一起使用可能会引起一些困惑。例如,MFC 用户遇到的一个问题是确定在代码中的哪个正确位置初始化崩溃报告库。
本文旨在作为入门教程。我用于演示的源代码和二进制文件附在文章中。CrashRpt
库(v.1.3.0)的代码也附在其中,但建议您从外部网站下载最新版本。
在本教程结束时,我将为有兴趣的读者提供一些链接,他们可以进一步了解更高级的主题,例如在多线程应用程序中使用 CrashRpt
、通过 Internet 将错误报告发送到 HTTP 服务器、使用命令行工具后处理错误报告等等。
注意:您还可以参考 Mike Carruth 的文章 使用 CrashRpt 库为您的应用程序添加崩溃报告,这篇文章稍微过时,但包含许多关于 CrashRpt 用法的有趣细节。
将 CrashRpt 集成到您的应用程序中
首先,我们将创建一个简单的文档-视图 MFC 应用程序,该应用程序在通过 *文件->保存* 菜单保存文档时总是会崩溃。
简单的 MFC 应用程序
在本教程中,我们将在 Visual Studio 2005 中从头开始创建一个非常简单的 MFC 应用程序。为此,我们打开 Visual Studio 窗口,然后打开菜单 **文件**,并从菜单中选择 **新建->项目…**。
在出现的 **新建项目** 对话框中,我们从模板列表中选择 MFC 应用程序,然后在 **名称** 字段中输入 SimpleApp
,然后按 OK 按钮。然后,当 **MFC 应用程序向导** - SimpleApp
对话框出现时,我们只需单击 **完成** 按钮即可生成新 SimpleApp
应用程序的项目文件。
现在,我们将添加一个会使应用程序崩溃的代码。当然,我们可以将这种不良代码插入到我们应用程序的任何地方(就像在现实生活中一样),但为了简单起见,我们将使我们的应用程序在保存文档时崩溃。为此,在 SimpleAppDoc.cpp 文件中,将 CSimpleAppDoc::Serialize()
方法修改为如下方式:
// CSimpleAppDoc serialization
void CSimpleAppDoc::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
// TODO: add storing code here
int* p = NULL;
*p = 13;
}
else
{
// TODO: add loading code here
}
}
上面的方法包含一个 `NULL` 指针赋值,执行时会引发访问冲突异常。
注意:如果您想知道还有哪些代码可能导致应用程序崩溃,可以参考文章 使您的 C++ 代码更健壮。
最后,我们按 **F5** 编译并运行创建的应用程序。应用程序窗口应该会出现(见下图)。
要查看崩溃时发生的情况,在 SimpleApp
窗口中,我们打开菜单 *文件* 并选择 *保存*。我们输入一个文件名并按 *保存* 按钮。应用程序将因错误消息而终止。
您可以在本教程中找到我们刚刚创建的应用程序(SimpleApp.zip 存档)。
现在,我们将安装 CrashRpt
库。在此演示中,我们将使用撰写本教程时可用的最新版本 CrashRpt
- v.1.3.0。
下载 CrashRpt
首先,我们应该获取 CrashRpt
库的源代码。您可以 在此处 下载最新的 CrashRpt
分发存档。存档使用 7z 格式,因此我们可以使用 7zip 工具解压存档。我们将其解压到某个文件夹,例如 C:\CrashRpt。
注意:CrashRpt
v.1.3.0 的源代码也附在本篇文章中,但建议您从 CrashRpt
项目网站获取最新版本。
让我们看一下 CrashRpt
文件夹。它包含几个子文件夹和文件。
bin 子文件夹包含已编译的 CrashRpt
二进制文件(CrashRpt1300.dll、CrashSender1300.exe 等)。CrashRpt
由两个核心模块组成:CrashRpt1300.dll 和 CrashSender1300.exe。CrashRpt1300.dll 包含拦截客户端软件中异常的功能。CrashSender1300.exe 包含压缩并向软件支持团队发送错误报告的功能。CrashRpt
分成这两个模块是为了能够关闭已崩溃的应用程序,并在后台继续发送错误报告到 CrashSender1300.exe 中。
include 和 lib 子文件夹包含头文件和导入库文件。我们稍后将在编译和链接 SimpleApp
应用程序时需要这些文件。
lang_files 子文件夹包含名为 crashrpt_lang_XX.ini 的语言 INI 文件,其中 XX 是语言缩写。INI 文件包含 CrashRpt
对话框的本地化字符串,因此您可以将其本地化为您的首选语言。
顶级文件夹包含文件 CrashRpt_vs2010.sln,这是 Visual Studio 2010 的 CrashRpt
解决方案文件。如果您使用 Visual Studio 2010,可以双击此文件并参考下面的 **编译 CrashRpt** 部分。但在本教程中,我将展示如何为旧版本 Visual Studio 2005 生成 CrashRpt
解决方案文件。
使用 CMake 生成 CrashRpt 项目文件
我们将使用 CMake 跨平台构建系统来非常轻松地生成 CrashRpt
解决方案文件。如果您没有 CMake,请在此处 下载其安装程序 并将 CMake 安装到您的计算机上。我当前使用的是最新版本 CMake 2.8.7。
接下来,我们将通过打开 *开始* 菜单并选择 **CMake 2.8->CMake (cmake-gui)** 来运行 CMake-GUI 向导。CMake 对话框应该会出现。在对话框中,我们需要提供我们解压 CrashRpt
存档的文件夹路径(在本例中为 C:\CrashRpt)。将此路径输入到“**源代码在哪里:**”和“**构建二进制文件在哪里:**”字段中,如图所示,然后按 **配置** 按钮。
这时会弹出生成器选择对话框,我们需要从下拉列表中选择 **Visual Studio 8 2005**,然后按 **完成** 按钮。然后按 **生成** 按钮。如果一切正常,您应该会看到“**生成完成**”消息。现在,转到 C:\CrashRpt 文件夹,您应该能看到 CrashRpt.sln 文件。双击该文件在 Visual Studio 中打开它。
编译 CrashRpt
强烈建议您自己编译 CrashRpt
,如果您希望 CrashRpt
处理可能发生在 C 运行时(CRT)库中的异常。CrashRpt
分发存档已包含已编译的 CrashRpt
二进制文件,但建议不要将其与您的软件一起使用,因为您的软件可能使用不同的 C 运行时 DLL,而 CrashRpt
将无法拦截您的 C 运行时库中的异常。
编译 CrashRpt
非常简单——您只需选择 **Release** 配置并按 **F7** 即可。如果一切正常,您应该可以在 bin 子文件夹中找到 CrashRpt
二进制文件。
使用 CrashRpt API
CrashRpt
的 include 和 lib 文件夹添加到 Visual Studio 的搜索路径中,以便 Visual C++ 编译器和链接器知道 CrashRpt
include 和 lib 文件的位置。我们通过在 Visual Studio 窗口中打开菜单 *工具->选项* 来实现此目的。然后在出现的对话框中,选择 **项目和解决方案->VC++ 目录**。最后,在 **显示目录** 下拉列表中,选择 **包含文件**,然后将 C:\CrashRpt\include 目录的路径添加到列表中。在 **显示目录** 下拉列表中,选择 **库文件**,然后将 C:\CrashRpt\lib 目录的路径添加到列表中。为了能够使用 CrashRpt
API 函数,我们在 SimpleApp.cpp 文件的开头包含 CrashRpt.h 头文件。
#include <CrashRpt.h>
当应用程序启动时,我们需要初始化 CrashRpt
。我们通过将一些 CrashRpt
函数插入到 SimpleApp
应用程序代码中来实现这一点。但我们需要将它们插入到称为 CWinApp::Run()
方法的正确位置。我们将以下代码插入到 SimpleApp.cpp 文件中:
int CSimpleAppApp::Run()
{
BOOL bRun;
BOOL bExit=FALSE;
while(!bExit)
{
bRun= CWinApp::Run();
bExit=TRUE;
}
return bRun;
}
我们重写了 CWinApp::Run()
方法。CWinApp::Run()
方法在 MFC 应用程序启动时被调用,所以这是初始化 CrashRpt
的正确位置。
接下来,我们通过调用 crInstall()
函数并使用 CR_INSTALL_INFO
结构传递配置参数来初始化 CrashRpt
。下面是插入了 CrashRpt
API 函数的 Run()
方法:
int CSimpleAppApp::Run()
{
// Install crash reporting
CR_INSTALL_INFO info;
memset(&info, 0, sizeof(CR_INSTALL_INFO));
info.cb = sizeof(CR_INSTALL_INFO);
info.pszAppName = _T("SimpleAp"); // Define application name.
info.pszAppVersion = _T("1.0.0"); // Define application version.
// URL for sending error reports over HTTP.
info.pszUrl = _T("http://someserver.com/crashrpt.php");
// Install all available exception handlers.
info.dwFlags |= CR_INST_ALL_POSSIBLE_HANDLERS;
// Use binary encoding for HTTP uploads (recommended).
info.dwFlags |= CR_INST_HTTP_BINARY_ENCODING;
// Provide privacy policy URL
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;
}
// Take screenshot of the app window at the moment of crash
crAddScreenshot2(CR_AS_MAIN_WINDOW|CR_AS_USE_JPEG_FORMAT, 95);
BOOL bRun;
BOOL bExit=FALSE;
while(!bExit)
{
bRun= CWinApp::Run();
bExit=TRUE;
}
// Uninstall crash reporting
crUninstall();
return bRun;
}
在上面的代码中,我们安装了应用程序中所有可用的异常处理器。我们指定了应用程序的名称和版本,因为这些信息是识别发送报告的应用程序所必需的。我们提供了一个 URL,用于使用二进制传输编码将错误报告传输到 HTTP 服务器。我们还提供了一个隐私政策 URL。
我们还调用了 crAddScreenshot2()
函数。我们指示它将应用程序窗口的屏幕截图添加到错误报告中。屏幕截图将使用 95% 质量的压缩 JPEG 格式,以减小图像大小。
最后,我们在退出 Run()
方法之前调用 crUninstall()
函数来取消初始化库。
好的,现在我们需要将 CrashRpt1300.lib 添加到项目的输入库列表中。在解决方案资源管理器窗口中,右键单击项目节点,然后从上下文菜单中选择属性项。然后打开 **配置属性->链接器->输入->附加依赖项**,然后将 CrashRpt1300.lib 添加到库列表中。
选择 **Release** 构建配置并按 F7 生成项目。
运行应用程序
我们几乎可以运行 SimpleApp
应用程序了。但在执行此操作之前,我们应该将 C:\CrashRpt\bin 文件夹中的以下文件复制到应用程序可执行文件所在的文件夹:
CrashRpt1300.dll
CrashSender1300.exe
dbghelp.dll
crashrpt_lang.ini
这些文件对于 CrashRpt
的正常运行是必需的。CrashRpt1300.dll 和 CrashSender1300.exe 文件是核心 CrashRpt
模块。dbghelp.dll 文件是 Microsoft 调试帮助库,CrashRpt
依赖于此模块。crashrpt_lang.ini 文件包含 CrashRpt
对话框的本地化字符串,因此您可以将其本地化为您的首选语言。
复制文件后,运行 SimpleApp.exe 文件。在出现的 SimpleApp
窗口中,打开 **文件** 菜单并选择 **保存**。输入一个文件名并按 **保存** 按钮。当发生访问冲突时,您应该会看到一个外观漂亮的 CrashRpt
**错误报告** 窗口,如图所示:
让我们回顾一下错误报告窗口上显示的内容。
**隐私政策** 链接允许查看我们在收集用户数据时使用的隐私政策。隐私政策通常说明我们将数据用于改进软件,并且不将数据出售或以其他方式传输给第三方。
我们看到报告包含 165 KB 的数据。这对于通过 Internet 传输来说相当小且可接受。CrashRpt
库可以将数据作为对 HTTP 服务器的请求或作为带有附件的电子邮件进行传输。接收者将错误报告作为包含多个文件的压缩 ZIP 存档接收。
要将生成的错误报告发送到您指定的 HTTP 服务器,请按 **发送报告** 按钮。如果您不想发送报告,请按 **关闭程序** 按钮。请注意,在本教程中,我没有展示如何通过 Internet 发送错误报告,您应该提供一个真实的接收者地址来发送错误报告作为电子邮件和/或配置一个服务器端脚本来通过 HTTP 连接发送错误报告。
通过单击 **此报告包含什么?** 链接,您可以查看生成的错误报告的内容。在下图中,您可以看到我们生成的报告包含三个文件:crashdump.dmp(崩溃最小转储)、crashrpt.xml(崩溃描述 XML)和 screenshot0.jpg(桌面屏幕截图)。
可以使用崩溃最小转储文件来调试崩溃。您可以双击 crashdump.dmp 文件在 Visual Studio 中打开它,并查看异常发生的代码行。
每个文件在错误报告中还有一个美观的十六进制、文本或图像预览。下图显示了我们应用程序窗口的 JPEG 屏幕截图的预览,这可能有助于重现崩溃。
结论
崩溃报告允许您自动收集有关软件错误的技术信息,以便以后在开发人员端进行后处理。在本教程中,我演示了如何将 CrashRpt 崩溃报告库集成到 MFC 应用程序中。
本教程非常简单,仅供初学者使用。在本教程中,我没有涵盖将 CrashRpt
安装到多线程应用程序、将自定义文件添加到错误报告、通过 Internet 以电子邮件消息或 HTTP 服务器请求的形式发送错误报告以及使用命令行工具分析收到的错误报告等高级主题。有兴趣的读者可以在 CrashRpt
在线文档中找到有关这些和其他主题的更多信息。
对于那些想更好地理解 Visual C++ 中的异常处理的人,我推荐文章 Visual C++ 中有效的异常处理,其中我详细描述了 CrashRpt
如何在 C++ 程序中捕获异常。如果您有兴趣了解上面图中显示的十六进制文件预览是如何工作的,可以参考文章 FilePreviewCtrl – 以文本、十六进制和图像格式预览文件。
历史
- 2012 年 1 月 1 日 - 首次发布