如何在发布后程序中捕获程序的崩溃和异常往往是比较麻烦的事情,一般采用日志记录的方法来记录程序运行的每个流程,但是通常为了程序运行的性能,日志记录的方法只是记录程序运行的每个主要的处理流程,不能进行具体详细的记录,比如for 循环中的崩溃记录。C++语言中调用window API函数CreateFile()和MiniDumpWriteDump(),可以方便的记录程序崩溃时的Dump信息,并保持dump文件,根据dump文件对应的源码工程和.pdb文件,我们就可以快速的定位到程序崩溃的源码位置,极大的提高了调试代码的效率。然而,Dump记录程序崩溃的方法不是万能的,有时候一些数组越界、容器访问异常等致命的问题,也不一定能准确的记录下来。总之,Dump文件记录程序崩溃的方法是开放人员常用的方法,并且能捕获大多数的程序崩溃问题。
下面是一段记录程序崩溃时生成Dump文件的方法,只要在工程中包含这个头文件代码,然后在程序构造函数中声明 RunCrashHandler(); 即可在程序崩溃时记录生成的Dump文件:
#ifndef DUMPFILE_H
#define DUMPFILE_H
#include <Windows.h>
#include <tchar.h>
#include <direct.h>
#include <Dbghelp.h>
using namespace std;
#pragma comment(lib, "Dbghelp.lib")
namespace NSDumpFile
{
void CreateDumpFile(LPCWSTR lpstrDumpFilePathName, EXCEPTION_POINTERS *pException)
{
// 创建Dump文件
HANDLE hDumpFile = CreateFile(lpstrDumpFilePathName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
// Dump信息
MINIDUMP_EXCEPTION_INFORMATION dumpInfo;
dumpInfo.ExceptionPointers = pException;
dumpInfo.ThreadId = GetCurrentThreadId();
dumpInfo.ClientPointers = TRUE;
// 写入Dump文件内容
MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, MiniDumpNormal, &dumpInfo, NULL, NULL);
CloseHandle(hDumpFile);
}
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI MyDummySetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
{
return NULL;
}
BOOL PreventSetUnhandledExceptionFilter()
{
HMODULE hKernel32 = LoadLibrary(_T("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;
}
LONG WINAPI UnhandledExceptionFilterEx(struct _EXCEPTION_POINTERS *pException)
{
//TCHAR szMbsFile[MAX_PATH] = { 0 };
//::GetModuleFileName(NULL, szMbsFile, MAX_PATH);
//TCHAR* pFind = _tcsrchr(szMbsFile, '\\');
//if (pFind)
//{
//*(pFind + 1) = 0;
//_tcscat_s(szMbsFile, _T("CrashDumpFile.dmp"));
//}
//创建 dmp 文件夹
char *_Path = "dumps";
int retval = _mkdir(_Path);
TCHAR szFileName[MAX_PATH] = { 0 };
TCHAR* szVersion = _T("Reg");
SYSTEMTIME stLocalTime;
GetLocalTime(&stLocalTime);
wsprintf(szFileName, L"dumps/%s_%04d%02d%02d-%02d%02d%02d.dmp",
szVersion, stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,
stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond);
CreateDumpFile(szFileName, pException);
// TODO: MiniDumpWriteDump
FatalAppExit(-1, _T("Fatal Error! Dump Create Success!"));
return EXCEPTION_CONTINUE_SEARCH;
}
void RunCrashHandler()
{
SetUnhandledExceptionFilter(UnhandledExceptionFilterEx);
PreventSetUnhandledExceptionFilter();
}
};
#define DeclareDumpFile() NSDumpFile::RunCrashHandler();
#endif //DUMPFILE_H