场景:
1. Release的程序崩溃时,崩溃报告能够让开发者查明代码哪里出了问题,用处大大的。
2. 仅仅实用VS的编译器才支持,所以MinGW就无缘了。
3. 使用了未处理异常过滤处理函数.
4. 生成的.dmp文件用zlib库压缩, 用到下面的ZipHelper类,编译时还是须要zlib库和dbghelp.lib
http://blog.****.net/infoworld/article/details/41290969
5. 使用方式就是把DbgReport作为app类的成员变量,或者文件范围的全局变量初始化后,在程序执行開始前调用
RegisterCrashFilter
6. 更新: 添加VC CRT异常捕抓. 2015-09-25
參考:
http://blog.****.net/limiteee/article/details/8472179
bas_dbg_report.h
#ifndef __BAS_DBG_REPORT
#define __BAS_DBG_REPORT #include "bas_exp.h" //1.能够自己改动參数,加入额外信息.
typedef void (*BASReportCallbackFunc)(const wchar_t* dump_zip_path); class LIB_BASIC BASDbgReport
{
public:
void RegisterCrashFilter(const wchar_t* dump_path,BASReportCallbackFunc func); }; #endif
bas_dbg_report.cpp
#include "basic/bas_dbg_report.h"
#include <Windows.h>
#include <DbgHelp.h> #include "basic/bas_utility_string.h"
#include "basic/bas_wrap_object.h"
#include "basic/bas_utility_zip.h" static std::wstring gDumpPath;
static std::wstring gDumpZipPath; static BASReportCallbackFunc gReportCallbackFunc = NULL; static BOOL IsDataSectionNeeded(const WCHAR* pModuleName)
{
if(pModuleName == NULL)
{
return FALSE;
} WCHAR szFileName[_MAX_FNAME] = L"";
_wsplitpath(pModuleName, NULL, NULL, szFileName, NULL); if(wcsicmp(szFileName, L"ntdll") == 0)
return TRUE; return FALSE;
} static BOOL CALLBACK MiniDumpCallback(PVOID pParam,
const PMINIDUMP_CALLBACK_INPUT pInput,
PMINIDUMP_CALLBACK_OUTPUT pOutput)
{
if(pInput == 0 || pOutput == 0)
return FALSE; switch(pInput->CallbackType)
{
case ModuleCallback:
if(pOutput->ModuleWriteFlags & ModuleWriteDataSeg)
if(!IsDataSectionNeeded(pInput->Module.FullPath))
pOutput->ModuleWriteFlags &= (~ModuleWriteDataSeg);
case IncludeModuleCallback:
case IncludeThreadCallback:
case ThreadCallback:
case ThreadExCallback:
return TRUE;
default:;
} return FALSE;
} static LONG WINAPI TopLevelUnhandledExceptionFilter(PEXCEPTION_POINTERS pExInfo)
{
HANDLE hFile = ::CreateFile( gDumpPath.c_str(), GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if( hFile != INVALID_HANDLE_VALUE)
{
MINIDUMP_EXCEPTION_INFORMATION einfo;
einfo.ThreadId = ::GetCurrentThreadId();
einfo.ExceptionPointers = pExInfo;
einfo.ClientPointers = FALSE; MINIDUMP_CALLBACK_INFORMATION mci;
mci.CallbackRoutine = (MINIDUMP_CALLBACK_ROUTINE)MiniDumpCallback;
mci.CallbackParam = NULL; ::MiniDumpWriteDump(::GetCurrentProcess(), ::GetCurrentProcessId(), hFile,MiniDumpNormal,&einfo, NULL, &mci);
::CloseHandle(hFile);
}
//1.压缩dmp文件和其它
char* utf8 = BASUtilityString::ConvertUnicodeToUtf8(gDumpPath.c_str());
BASWrapMalloc wm1(utf8); BASUtilityZip z;
z.AddFile(utf8);
std::string output(utf8);
output.append(".zip"); wchar_t* unicode = BASUtilityString::ConvertUtf8ToUnicode(output.c_str());
BASWrapMalloc wm2(unicode); gDumpZipPath.append(unicode); z.ToZip(output.c_str());
if(gReportCallbackFunc)
{
gReportCallbackFunc(gDumpZipPath.c_str());
}
return EXCEPTION_EXECUTE_HANDLER;
} static LPTOP_LEVEL_EXCEPTION_FILTER WINAPI MyDummySetUnhandledExceptionFilter(
LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
{
return NULL;
} static BOOL PreventSetUnhandledExceptionFilter()
{
HMODULE hKernel32 = LoadLibrary(L"kernel32.dll");
if (hKernel32 == NULL) return FALSE;
void *pOrgEntry = GetProcAddress(hKernel32, "SetUnhandledExceptionFilter");
if(pOrgEntry == NULL) return FALSE;
unsigned char newJump[ 100 ];
DWORD dwOrgEntryAddr = (DWORD) pOrgEntry;
dwOrgEntryAddr += 5; // add 5 for 5 op-codes for jmp far
void *pNewFunc = &MyDummySetUnhandledExceptionFilter;
DWORD dwNewEntryAddr = (DWORD) pNewFunc;
DWORD dwRelativeAddr = dwNewEntryAddr - dwOrgEntryAddr; newJump[ 0 ] = 0xE9; // JMP absolute
memcpy(&newJump[ 1 ], &dwRelativeAddr, sizeof(pNewFunc));
SIZE_T bytesWritten;
BOOL bRet = WriteProcessMemory(GetCurrentProcess(),
pOrgEntry, newJump, sizeof(pNewFunc) + 1, &bytesWritten);
return bRet;
} void BASDbgReport::RegisterCrashFilter(const wchar_t* dump_path,BASReportCallbackFunc func)
{
#ifndef _DEBUG
gDumpPath.append(dump_path);
gReportCallbackFunc = func;
SetUnhandledExceptionFilter(TopLevelUnhandledExceptionFilter);
//BOOL bRet = PreventSetUnhandledExceptionFilter(); //这个部分系统会崩溃,临时不使用.
#endif
}