【C/C++|C++生成Dump文件】C++开发的应用程序避免不了各种崩溃,空指针,野指针,栈溢出等等。为了方便定位问题,我们一般在程序崩溃的时候自动生成一个dump文件,然后通过dump文件结合pdb来定位问题。c++生成dump文件的代码网上有很多,但大多使用起来不那么方便;通过本文介绍的方法只需要使用2个宏就可以实现自动生成dump的功能。
功能代码包含在EasyDump.h和EasyDump.cpp 2个文件中:
EasyDump.h
#pragma once#include
#include
#include namespace cpp4j {
typedef struct _EXCEPTION_POINTERS EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;
int __cdecl RecordExceptionInfo(PEXCEPTION_POINTERS pExceptPtrs, const TCHAR *szDumpNamePrefix);
TCHAR *lstrrchr(LPCTSTR string, int ch);
void DumpMiniDump(HANDLE hFile, PEXCEPTION_POINTERS excpInfo);
}#define WINMAIN_BEGIN(szDumpNamePrefix) \
int __96A9695E_RUN_WINMAIN_FUNC(HINSTANCE hInstance, LPTSTR lpCmdLine);
\
LONG WINAPI __96A9695E_UnhandledExceptionHandler( _EXCEPTION_POINTERS *pExceptionInfo ) \
{ \
OutputDebugString(TEXT("Create a dump file sine an exception occurred in sub-thread.\n"));
\
int iRet = cpp4j::RecordExceptionInfo(pExceptionInfo, szDumpNamePrefix);
\
return iRet;
\
} \
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) \
{ \
UNREFERENCED_PARAMETER(hPrevInstance);
\
UNREFERENCED_PARAMETER(nCmdShow);
\
::SetUnhandledExceptionFilter( __96A9695E_UnhandledExceptionHandler );
\
int ret = 0;
\
__try\
{\
ret = __96A9695E_RUN_WINMAIN_FUNC(hInstance, lpCmdLine);
\
}\
__except(cpp4j::RecordExceptionInfo(GetExceptionInformation(), szDumpNamePrefix))\
{\
OutputDebugString(TEXT("Create a dump file sine an exception occurred in main-thread.\n"));
\
}\
return ret;
\
}\
int __96A9695E_RUN_WINMAIN_FUNC(HINSTANCE hInstance, LPTSTR lpCmdLine) \
{#define WINMAIN_END }#define MAIN_BEGIN(szDumpName) \
int __96A9695E_RUN_MAIN_FUNC(int argc, _TCHAR* argv[]);
\
LONG WINAPI __96A9695E_UnhandledExceptionHandler( _EXCEPTION_POINTERS *pExceptionInfo ) \
{ \
OutputDebugString(TEXT("Create a dump file since an exception occurred in sub-thread.\n"));
\
int iRet = cpp4j::RecordExceptionInfo(pExceptionInfo, szDumpName);
\
return iRet;
\
} \
int _tmain(int argc, _TCHAR* argv[])\
{ \
::SetUnhandledExceptionFilter( __96A9695E_UnhandledExceptionHandler );
\
int ret = 0;
\
__try\
{\
ret = __96A9695E_RUN_MAIN_FUNC(argc, argv);
\
}\
__except(cpp4j::RecordExceptionInfo(GetExceptionInformation(), szDumpName))\
{\
OutputDebugString(TEXT("Create a dump file since an exception occurred in main-thread.\n"));
\
}\
return ret;
\
}\
int __96A9695E_RUN_MAIN_FUNC(int argc, _TCHAR* argv[]) \
{#define MAIN_END }
EasyDump.cpp
#include "EasyDump.h"
#include
#pragma comment(lib, "Dbghelp.lib")namespace cpp4j {
TCHAR *lstrrchr(LPCTSTR string, int ch) {
TCHAR *start = (TCHAR *)string;
while (*string++)
;
while (--string != start && *string != (TCHAR)ch)
;
if (*string == (TCHAR)ch)
return (TCHAR *)string;
return NULL;
} inline void DumpMiniDump(HANDLE hFile, PEXCEPTION_POINTERS excpInfo) {
if (!excpInfo) {
static int iTimes = 0;
if (iTimes++ > 1)
return;
__try {
RaiseException(EXCEPTION_BREAKPOINT, 0, 0, NULL);
}
__except (DumpMiniDump(hFile, GetExceptionInformation()),
EXCEPTION_CONTINUE_EXECUTION) {
}
}
else {
MINIDUMP_EXCEPTION_INFORMATION eInfo;
eInfo.ThreadId = GetCurrentThreadId();
eInfo.ExceptionPointers = excpInfo;
eInfo.ClientPointers = FALSE;
MiniDumpWriteDump(
GetCurrentProcess(),
GetCurrentProcessId(),
hFile,
MiniDumpNormal,
excpInfo ? &eInfo : NULL,
NULL,
NULL);
}
} int __cdecl RecordExceptionInfo(PEXCEPTION_POINTERS pExceptPtrs, const TCHAR *szDumpNamePrefix) {
static bool bFirstTime = true;
if (!bFirstTime)
return EXCEPTION_CONTINUE_SEARCH;
bFirstTime = false;
// Dmp文件命名:前缀_年月日.时.分.秒.毫秒.dmp
//
TCHAR szLocalTime[50] = { 0 };
SYSTEMTIME st;
GetLocalTime(&st);
StringCchPrintf(szLocalTime, 50, TEXT("%04d%02d%02d.%02d.%02d.%02d.%04d"), st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
TCHAR szExeDir[MAX_PATH + 1] = { 0 };
GetModuleFileName(NULL, szExeDir, MAX_PATH);
if (TCHAR *p = lstrrchr(szExeDir, TEXT('\\'))) {
*(p + 1) = 0;
}TCHAR szDumpFileName[MAX_PATH + 1] = { 0 };
_stprintf_s(szDumpFileName, MAX_PATH, TEXT("%s%s_%s.dmp"), szExeDir, szDumpNamePrefix, szLocalTime);
HANDLE hMiniDumpFile = CreateFile(
szDumpFileName,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
NULL);
if (hMiniDumpFile != INVALID_HANDLE_VALUE) {
DumpMiniDump(hMiniDumpFile, pExceptPtrs);
CloseHandle(hMiniDumpFile);
hMiniDumpFile = NULL;
}return EXCEPTION_EXECUTE_HANDLER;
}
}
使用方法:
#include "EasyDump.h"MAIN_BEGIN(TEXT("Test"))int i = 0;
int *p = &i;
p = NULL;
*p = 5;
return 0;
MAIN_END
使用MAIN_BEGIN替换main,WINMAIN_BEGIN替换WinMain即可。
MAIN_BEGIN中的参数为生成的dump文件的前缀,dump文件命名方式:Dmp文件命名: 前缀_年月日.时.分.秒.毫秒.dmp 如上面的代码,会在程序的当前目录生成一个名为Test_20171101.14.49.57.0264.dmp的dump文件。
推荐阅读
- c/c++|有感 Visual Studio 2015 RTM 简介 - 八年后回归 Dot Net,终于迎来了 Mvc 时代,盼走了 Web 窗体时代...
- C/C++|C/C++ basis 02
- Qt实战|Qt+OpenCV联合开发(二十一)--图像翻转与旋转
- Qt实战|Qt+OpenCV联合开发(十四)--图像感兴趣区域(ROI)的提取
- Qt实战|Qt+OpenCV联合开发(十三)--通道分离与合并
- opencv|Qt+OpenCV联合开发(十六)--图像几何形状绘制
- Qt实战|Qt+OpenCV联合开发(十七)--随机数与随机颜色
- SNAT的MASQUERADE地址选择与端口选择
- IPTABLES的连接跟踪与NAT分析
- IPVS分析