C/C++|C++生成Dump文件

【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文件。


    推荐阅读