crashrpt

article/2025/10/19 0:00:14

        今天原本打算在谷歌上搜索处理SEH的文章,以使我不需要在每一个线程中使用__try{}__except()代码块包裹代码的情况下,就能在任意线程抛出SEH时生成MiniDump文件。不过最后的结果是处理SEH的文章没有搜索出几篇,却幸运的搜索出了满足我需要的工具crashrpt。

        crashrpt是一个包含能够在程序出现各种类型未处理异常时生成程序错误报告,然后将该报告按照指定的方式(例如HTTP或者SMTP)发送给开发者,最后分析这些信息的工具。crashrpt由3个部分组成,错误报告生成库CrashRpt,我们需要在自己的程序中使用该库捕获我们的程序没有处理的异常,在该库捕获到这些未处理的异常后,CrashRpt会生成MiniDump文件,并将和你使用该库指定的信息(例如日志文件和屏幕截图等)一起打包成错误报告。CrashRpt库支持处理我所知道的所有Windows C/C++程序抛出的各类异常,例如我前面提到过的SEH,它还能捕获C++异常、信号和调用各类CRT库中的函数出现的错误。异常信息发送工具CrashSender,该工具能够按照我们使用CrashRpt设置的方式,将生成的错误报告按照我们指定的方式(HTTP、SMTP或者MAPI)发送给我们。自动异常信息处理工具crprober,该工具能够在后台接收CrashSender发送给我们的错误报告,通过分析错误报告后以文本的形式输出程序的异常信息。关于crashrpt更详细的介绍,可以参考面https://code.google.com/p/crashrpt/以及http://crashrpt.sourceforge.net/docs/html/getting_started.html。

        为了使用crashrpt,我们首先需要在https://code.google.com/p/crashrpt上下载crashrpt的最新版本,在我写这篇文章时的最新版本是1.3.0。下载解压后的目录如下图所示:

其中bin目录中包含使用vc10编译出来的所有crashrpt相关库和程序,include和lib目录中包含了开发所需要的头文件以及lib文件。如果你不介意程序在发布时带上vc10的运行库,或者你的程序就是用vc10开发的,你可以直接使用这些编译好的二进制文件。如果你想crashrpt和你的程序依赖相同的vc运行库,那么你需要使用你的vc重新编译crashrpt。对于使用除vc10之外的其它vc版本的朋友,如果要编译crashrpt,则需要使用开源交叉编译工具cmake,我们需要使用cmake生成和你vc版本相同的解决方案以及工程文件。首先在http://cmake.org/cmake/resources/software.html中下载并安装适合你系统的最新版本cmake。安装完成后,运行cmake-gui,在where is the source code文本框以及where to build the binaries文本框中输入crashrpt的顶层目录,也就是包含文件CMakeLists.txt的目录,然后点击Configure按钮,然后在弹出的对话框中选择你的vc版本并点击finish,则出现如图所示的输出:


在列表框中选择和你的vc版本匹配的选项,最后点击Gnerate按钮,就可以生成与你vc版本相匹配的解决方案和工程文件了。使用vc打开生成的解决方案文件,在这里我使用的vc版本是vc9,该解决方案中有下图所示的工程:


在解决方案处点击右键菜单中的build,即可生成crashrpt。

       下面让我们来看一下如何使用crashrpt库来生成错误报告。首先我们需要声明一个CR_INSTALgL_INFO结构体,然后按照自己的需要对其进行设置之后,即可以使用crInstall函数向程序中安装crashrpt中的异常处理函数。调用该函数之后,如果程序中出现了未捕获的异常,则crashrpt会捕获该异常并生成MiniDump文件,关于CR_INSTALgL_INFO结构体的详细描述,请参考http://crashrpt.sourceforge.net/docs/html/struct_c_r___i_n_s_t_a_l_l___i_n_f_o_a.html。除了MiniDump文件之外,我们还可以通过调用 crAddFile2函数够将指定的文件加入到错误报告中,例如我们可以将程序相关的日志文件加入到错误报告中,以便我们更好的分析程序的内部状态,然后通过这些信息更快的找到程序出错的原因;除了能够添加指定的文件以外,我们还能够通过调用 crAddScreenshot函数添加屏幕截图,这样在程序崩溃的时候,我们能够将当时的屏幕截图包含到错误报告中;有时候运行程序的硬件也可能是导致程序崩溃的原因,我们可以通过调用 crAddProperty函数,将自定义信息添加到crashrpt生成的错误报告里包含的xml描述文件中;最后,我们还可以调用crAddRegKey函数将注册表的相关信息包含到错误报告中;请记住在程序结束之前调用crUninstall函数清理crashrpt所使用的相关资源。关于crashrpt使用的更详细介绍,请参考http://crashrpt.sourceforge.net/docs/html/using_crashrpt_api.html

    现在让我们来看一个使用crashrpt库的示例MyApp,程序MyApp拥有2个线程,主线程负责与用户进行交互,另外一个工作线程负责处理一些需要花费大量时间才能完成的操作。程序还将创建一个日志文件,我们可以使用crashrpt库在程序崩溃的时候将该文件和MiniDump文件一起打包发送给我们,以便于我们更好地分析出程序崩溃的原因。程序的代码如下所示:

#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include "CrashRpt.h" // 包含crashrpt库使用所需要的头文件 FILE* g_hLog = NULL; // 日志文件句柄// 程序崩溃时由crashrpt调用的回调函数
BOOL WINAPI CrashCallback(LPVOID /*lpvState*/)
{  // 需要在这里关闭日志文件句柄,否则crashrpt无法对处于占用状态的文件进行操作if(g_hLog!=NULL){fclose(g_hLog);g_hLog = NULL;}// 返回TRUE, 由crashrpt生成错误报告return TRUE;
}
// 日志函数
void log_write(LPCTSTR szFormat, ...)
{if (g_hLog == NULL) return;va_list args; va_start(args); _vftprintf_s(g_hLog, szFormat, args);fflush(g_hLog);
}// 线程处理函数
DWORD WINAPI ThreadProc(LPVOID lpParam)
{// 在该线程中安装crashrpt库对未处理异常的处理
  crInstallToCurrentThread2(0);log_write(_T("Entering the thread proc\n"));for(;;){// 在这里模拟一处内存越界int* p = NULL;*p = 13;}    log_write(_T("Leaving the thread proc\n"));// 清理crashrpt资源crUninstallFromCurrentThread();    return 0;
}int _tmain(int argc, _TCHAR* argv[])
{  // 设置crashrpt的各项参数CR_INSTALL_INFO info;  memset(&info, 0, sizeof(CR_INSTALL_INFO));  info.cb = sizeof(CR_INSTALL_INFO);    info.pszAppName = _T("MyApp");  info.pszAppVersion = _T("1.0.0");  info.pszEmailSubject = _T("MyApp 1.0.0 Error Report");  info.pszEmailTo = _T("myapp_support@hotmail.com");    info.pszUrl = _T("http://myapp.com/tools/crashrpt.php");  info.pfnCrashCallback = CrashCallback;   info.uPriorities[CR_HTTP] = 3;  // 首先使用HTTP的方式发送错误报告info.uPriorities[CR_SMTP] = 2;  // 然后使用SMTP的方式发送错误报告  info.uPriorities[CR_SMAPI] = 1; //最后尝试使用SMAPI的方式发送错误报告    // 捕获所有能够捕获的异常, 使用HTTP二进制编码的方式传输info.dwFlags |= CR_INST_ALL_POSSIBLE_HANDLERS;info.dwFlags |= CR_INST_HTTP_BINARY_ENCODING; info.dwFlags |= CR_INST_APP_RESTART; info.dwFlags |= CR_INST_SEND_QUEUED_REPORTS; info.pszRestartCmdLine = _T("/restart");// 隐私策略URLinfo.pszPrivacyPolicyURL = _T("http://myapp.com/privacypolicy.html"); int nResult = crInstall(&info);    if(nResult!=0)  {    TCHAR szErrorMsg[512] = _T("");        crGetLastErrorMsg(szErrorMsg, 512);    _tprintf_s(_T("%s\n"), szErrorMsg);    return 1;} // 添加日志文件到错误报告中crAddFile2(_T("log.txt"), NULL, _T("Log File"), CR_AF_MAKE_FILE_COPY);    // 添加程序崩溃时的截屏到错误报告中crAddScreenshot(CR_AS_VIRTUAL_SCREEN);   // 添加任意的信息到错误报告中,这里以显卡信息作为示例crAddProperty(_T("VideoCard"), _T("nVidia GeForce 8600 GTS"));errno_t err = _tfopen_s(&g_hLog, _T("log.txt"), _T("wt"));if(err!=0 || g_hLog==NULL){_tprintf_s(_T("Error opening log.txt\n"));return 1; // Couldn't open log file}log_write(_T("Started successfully\n"));HANDLE hWorkingThread = CreateThread(NULL, 0, ThreadProc, (LPVOID)NULL, 0, NULL);log_write(_T("Created working thread\n"));TCHAR* szFormatString = NULL;_tprintf_s(szFormatString);WaitForSingleObject(hWorkingThread, INFINITE);log_write(_T("Working thread has exited\n"));if(g_hLog!=NULL){fclose(g_hLog);g_hLog = NULL;}crUninstall();return 0;
}
该示例程序中有几点需要注意的地方:

1.如果想要在错误报告中包含日志文件,请记住一定要使用类似于示例中的CrashCallBack函数来设置CR_INSTALL_INFO中的pfnCrashCallback域,并在函数中关闭该日志文件的句柄。

2.根据我的使用经验,其实不需要在线程中使用crInstallToCurrentThread2/crUninstallFromCurrentThread这一对函数来安装异常处理过程,只要在主线程中调用了crInstall。就能够捕获到程序中所有线程中未处理的异常。

3.调用crInstall出错的原因一般是没有将CrashRptXXXX.dll、CrashSenderXXXX.exe以及crashrpt_lang.ini放到正确的路径中,在默认情况下,该路径即是和应用程序相同的路径。其中的XXXX指的是crashrpt的版本号,这篇文章中的版本号为1300。

关于该示例的原文介绍,请参考http://crashrpt.sourceforge.net/docs/html/simple_example.html

        最后发一段我使用crashrpt的代码块,我使用的目的是将程序交给测试人员进行测试时,如果程序崩溃后,crashrpt将程序的错误报告保存到本地,测试人员发现程序崩溃后,将该报告发给我进行调试。代码如下所示:

int main(int argc, char **argv)
{
#if defined(WIN32) && defined(USE_CRASHRPT)CR_INSTALL_INFO info = {0};info.cb = sizeof(CR_INSTALL_INFO);info.pszAppName = TEXT("xxx");info.pszAppVersion = TEXT("0.1.0");   info.dwFlags |= CR_INST_ALL_POSSIBLE_HANDLERS;info.dwFlags |= CR_INST_DONT_SEND_REPORT;info.pszErrorReportSaveDir = TEXT("./xxx");if (EXIT_SUCCESS != crInstall(&info)){TCHAR errorMsg[512];crGetLastErrorMsg(errorMsg, 512);std::cerr << errorMsg;return EXIT_FAILURE;}
#endifint ret = mainImpl(argc, argv);#if defined(WIN32) && defined(USE_CRASHRPT)crUninstall();
#endifreturn ret;
}

     crashrpt是一个功能很强大的错误报告生成、发送以及分析工具。由于我的使用比较简单,所以我这里介绍的只是crashrpt功能的一小部分,按照crashrpt文档中的描述,crashrpt完全可以在使用http发送错误报告时,与我们所使用的BUG管理系统进行联动,我认为这样可以极大的提升BUG的修改效率,如果对crashrpt有兴趣的朋友,可以参考 http://crashrpt.sourceforge.net/docs/html/index.html进行更深入的学习。


http://chatgpt.dhexx.cn/article/nOOMhrGX.shtml

相关文章

Windows平台崩溃转储系统crashrpt的使用

概述 CrashRpt 是一个免费的、轻量级的开源错误报告库开源库&#xff0c;旨在拦截C程序中的异常&#xff0c;收集有关崩溃的技术信息并通过互联网向软件供应商发送错误报告&#xff0c;用于在 Microsoft Visual Studio IDE 中创建并在 Windows 中运行C应用程序。&#xff08;不…

Android APK及签名

APK是AndroidPackage的缩写&#xff0c;即Android安装包(apk)。APK是类似Symbian Sis或Sisx的 文件格式。通过将APK文件直接传到 Android模拟器或Android手机中执行即可安装。apk文件和sis一样&#xff0c;把 android sdk编译的工程打包成一个安装程序文件&#xff0c;格式为ap…

安卓应用签名

安卓应用release模式是需要签名的&#xff0c;否则无法安装&#xff0c;debug模式不需要的 签名可以用keytool生成密钥文件&#xff0c;然后在build.gradle中指定即可。 一、生成密钥文件 keytool -genkey -alias noalias -keypass abcd1234 -keyalg RSA -keysize 2048 -valid…

Android为App签名(为apk签名)

写博客是一种快乐&#xff0c;前提是你有所写&#xff0c;与人分享&#xff0c;是另一种快乐&#xff0c;前提是你有舞台展示&#xff0c;CSDN就是这样的舞台。 这篇文章是android开发人员的必备知识&#xff0c;是我特别为大家整理和总结的&#xff0c;不求完美&#xff0c;但…

AndroidStudio应用签名

1、新建存放签名文件的文件夹 Build 2、初次没有账号 点击Create new 创建 3、签名成功后会在build。gradle 生成下面的数据 4、AS中调出黑窗口写入 keytool -list -v -keystore “L:/boxin/bx.jks” 签名地址 输入密码 5、找到MD5 SHA1 后面的密钥就是你的应用签名

android app签名详解

本文及文中图片转自&#xff1a;https://mp.weixin.qq.com/s?__bizMzIwMzYwMTk1NA&mid2247493825&idx1&sne926da39c6bd51397851d7e330d6ba24&chksm96ce498ca1b9c09a7865264b26eadcf27bd012e9acc999b3eb3ca5c440266642763b6be27416&mpshare1&scene23&a…

Android应用签名之AS签名

废话不多说直接上图 如之前未生成jks文件则&#xff0c;点击create new .. 这里只要输入几个必要项 Key store path&#xff08;生产key文件的保存路径 &#xff09; Key store password&#xff08;key 存储密码&#xff09; Key alias&#xff08;key别名&#xff09; Key p…

安卓应用程序的签名

签名安卓应用程序 Android应用以它的包名作为唯一标识。如果在同一部手机上安装两个包名相同的应用&#xff0c;后面安装的应用就会覆盖前面安装的应用。为了避免这种情况的发生&#xff0c;Android要求对作为产品发布的应用进行签名。 签名主要有如下两个作用&#xff1a; 1.确…

android app签名方法

1、生成签名文件 在android studio 里操作如下&#xff1a; 填完即可。 会产生这个签名文件。 2、签名app 到jdk的bin文件夹下输入下面命令&#xff1a; jarsigner -verbose -keystore D:\appkey\geming.jks D:\svn\Bigdata\IOT\iot-village-app\village\platforms\android\…

Android应用签名

为了要签名&#xff1f; 开发Android的人这么多&#xff0c;完全有可能大家都把类名&#xff0c;包名起成了一个同样的名字&#xff0c;这时候如何区分&#xff1f;签名这时候就是起区分作用的。 由于开发商可能通过使用相同的Package Name来混淆替换已经安装的程序&#xff0c…

Android App签名

Andriod应用程序如果要在手机或模拟器上安装&#xff0c;必须要有签名&#xff01; 1.签名的意义   为了保证每个应用程序开发商合法ID&#xff0c;防止部分开放商可能通过使用相同的Package Name来混淆替换已经安装的程序&#xff0c;我们需要对我们发布的APK文件进行唯一…

Android开发给apk签名

前言 最近360加固助手签名突然收费了&#xff0c;加固完后要自己签名了。在此记录一下自己使用命令行签名步骤。 之后又更新了加固助手&#xff0c;点击自动签名提示如下&#xff1a; 之前没提示工具包手动签名&#xff0c;平时也就加固下应用&#xff0c;也没太注意工具包&…

Andorid进行签名的方式

目录 Android签名的简介 1.Android签名的作用 2.签名的方式 2.1签名方式的比较 3.在AS上操作的一般步骤(手动签名) 3.1 在AS操作步骤如下图 4.apk常用的签名方法(系统签名)有两种 4.1 使用工具对apk进行签名 4.2 在Ubuntu系统下&#xff0c;通过命令行进行签名 Androi…

Android APP的签名

Android APP的签名 Android项目以它的包名作为唯一的标识&#xff0c;如果在同一部手机上安装两个包名相同的APP&#xff0c;后者就会覆盖前面安装的应用。为了避免Android APP被随意覆盖&#xff0c;Android要求对APP进行签名。下面介绍对APP进行签名的步骤 1、选择builder菜…

keil中c语言定义数组,如题,KeilC51位变量能不能定义数组

公告&#xff1a; 为响应国家净网行动&#xff0c;部分内容已经删除&#xff0c;感谢读者理解。 话题&#xff1a;如题,KeilC51位变量能不能定义数组回答&#xff1a;试试呗我的结论是不行 会报错 error C168: array of bit 手册描述&#xff1a;An array may not have type bi…

【C 语言】数组 ( 数组类型表达 | 定义数组类型 )

文章目录 总结一、数组类型表达二、定义数组类型三、代码示例 总结 // 定义类数组数据类型 int [10] , 类型别名为 ArrayTypetypedef int (ArrayType)[10];// 与 int array[10] {0}; 作用相同ArrayType array2 {0};一、数组类型表达 C 语言中的 数据类型 分为 基础数据类型 ,…

JavaScript定义数组

JavaScript定义数组 JavaScript中定义数组的方法&#xff1a; 定义时直接给数组元素赋值 var arr[值1,值2,值3....]定义一个空数组 var arr[];定义一个空数组并通过索引来赋值 var arrnew Array(); arr[0]1; arr[1]2;提示&#xff1a; JavaScript为数组提供了一个length属…

用卷积公式计算“独立随机变量之和”的概率分布 Distribution of sum of independent Rayleigh random variables

https://math.stackexchange.com/questions/1019375/distribution-of-sum-of-independent-rayleigh-random-variables

信号处理的卷积公式

《信号与系统》关于卷积的公式&#xff1a; 离散 序列卷积和 周期卷积 循环卷积 连续 卷积积分

卷积基本计算公式

1. 理论公式 2. tensorflow中使用 输入图片大小 WW Filter大小 FF 步长strides S padding的像素数 P 输出大小为NxNpadding “SAME”时&#xff0c;会在图像的周围填 “0”&#xff0c;padding “VALID”则不需要&#xff0c;即 P0。一般会选“SAME”&#xff0c;以来减缓图…