在程序崩溃时,需要对异常的情况进行捕获,并捕获到的堆栈信息保持下来。Windows操作系统提供了一个API函数可以在程序crash之前有机会处理这些异常并生成dump文件。通过VS2013编辑器调试dump文件能快速定位到程序崩溃处的代码。
一、Windows API函数介绍
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter ( _In_LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter
) ;
参数:lpTopLevelExceptionFilter ,函数指针。当异常发生时,且程序不处于调试模式(在vs或者别的调试器里运行)则首先调用该函数。
注意:在调用者进程中的所有线程已经将来创建的线程的异常处理链顶函数都会被修改为当前设定的回调函数。
二、demo代码
#include "stdafx.h"
#include <windows.h>
#include <stdlib.h>
using namespace std;
#pragma comment(lib, "Dbghelp.lib")
#include <Dbghelp.h>
namespace DumpFile
{
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(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;
}
LONG WINAPI UnhandledExceptionFilterEx(struct _EXCEPTION_POINTERS *pException)
{
TCHAR szFileName[MAX_PATH] = { 0 };
SYSTEMTIME stLocalTime;
GetLocalTime(&stLocalTime);
wsprintf(szFileName, L"%04d%02d%02d-%02d%02d%02d-%ld-%ld.dmp",
stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,
stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond,
GetCurrentProcessId(), GetCurrentThreadId());
CreateDumpFile(szFileName, pException);
MessageBox(NULL, L" 运行中出现错误! ", L"123456", MB_OK);
FatalExit(-1);
return EXCEPTION_CONTINUE_SEARCH;
}
void RunCrashHandler()
{
SetUnhandledExceptionFilter(UnhandledExceptionFilterEx);
PreventSetUnhandledExceptionFilter();
}
};
#define ExceptionDumpFile() DumpFile::RunCrashHandler();
int _tmain(int argc, _TCHAR* argv[])
{
ExceptionDumpFile();
char* a = nullptr;
*a = 1;
return 0;
}
三、工程属性配置
1、打开工程->属性->链接器->调试->生成调试信息,修改成【是】(如下图)。
2、打开工程->属性->C/C++->常规->调试信息格式,修改成【程序数据库】(如下图)。注意:如果在这里不修改成【程序数据库】,在调试dump文件时无法定位到源文件代码中的。
四、VS2013调试dump文件
1、用VS2013打开dump文件,选择【设置符号路径】->【调试】->【符号】,把pdb、exe、dll文件路径添加到下图位置中。
2、添加源代码文件
1、选择【视图】->【解决方案资源管理器】,把解决方案生成出来。如下图:
2、选择【解决方案】->右键->【属性】->【调试源文件】,添加源文件所在目录添加到下图所示位置中。
源码工程路径:https://download.csdn.net/download/jgliuhui1988/85330167