使用函数UnhandledExceptionFilterEx()捕捉崩溃,在崩溃前重启程序。
函数
int RestartProcess(bool bNormal = false);
中默认参数bNormal用于控制崩溃之后是否自动结束程序,具体可以自测。
#include "MiniDump.h"
#include <windows.h>
#include <imagehlp.h>
#include <stdlib.h>
#include <tchar.h>
#pragma comment(lib, "dbghelp.lib")
int RestartProcess(bool bNormal = false);
inline BOOL IsDataSectionNeeded(const WCHAR* pModuleName)
{
if (pModuleName == 0)
{
return FALSE;
}
WCHAR szFileName[_MAX_FNAME] = L"";
//_wsplitpath(pModuleName, NULL, NULL, szFileName, NULL);
_wsplitpath_s(pModuleName, NULL, 0, NULL, 0, szFileName, 0, NULL, 0);
//if (wcsicmp(szFileName, L"ntdll") == 0)
if (_wcsicmp(szFileName, L"ntdll") == 0)
return TRUE;
return FALSE;
}
inline 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;
}
//创建Dump文件
inline void CreateMiniDump(EXCEPTION_POINTERS* pep, LPCTSTR strFileName)
{
HANDLE hFile = CreateFile(strFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if ((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE))
{
MINIDUMP_EXCEPTION_INFORMATION mdei;
mdei.ThreadId = GetCurrentThreadId();
mdei.ExceptionPointers = pep;
mdei.ClientPointers = FALSE;
MINIDUMP_CALLBACK_INFORMATION mci;
mci.CallbackRoutine = (MINIDUMP_CALLBACK_ROUTINE)MiniDumpCallback;
mci.CallbackParam = 0;
MINIDUMP_TYPE mdt = (MINIDUMP_TYPE)0x0000ffff;
MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &mdei, NULL, &mci);
CloseHandle(hFile);
}
}
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(szMbsFile, _T("CreateMiniDump.dmp"));
_tcscat_s(szMbsFile, _T("CreateMiniDump.dmp"));
CreateMiniDump(pException, szMbsFile);
}
// TODO: MiniDumpWriteDump
RestartProcess();
FatalAppExit(-1, _T("Fatal Error"));
return EXCEPTION_CONTINUE_SEARCH;
}
//运行异常处理
void RunCrashHandler()
{
static bool bIni = false;
if (!bIni)
{
SetUnhandledExceptionFilter(UnhandledExceptionFilterEx);
PreventSetUnhandledExceptionFilter();
bIni = true;
}
}
在程序框架中实现函数RestartProcess()如下:
int RestartProcess(bool bNormal = true)
{
PROCESS_INFORMATION info;
STARTUPINFO startup;
TCHAR szPath[128];
TCHAR *szCmdLine;
GetModuleFileName(AfxGetApp()-> m_hInstance, szPath, sizeof(szPath));
szCmdLine = GetCommandLine();
GetStartupInfo(&startup);
BOOL bSucc = CreateProcess(szPath, szCmdLine, NULL, NULL,
FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &startup, &info);
if(bNormal && bSucc)
{
CWnd *pWnd = AfxGetMainWnd();
if(pWnd != NULL)
{
pWnd->PostMessage(WM_CLOSE, 0, 0);
}
else
ExitProcess(-1);
}
else
ExitProcess(-1);
return 0;
}
测试方法,在程序初始化的时候调用RunCrashHandler(),然后实现任意一个崩溃即可,比如数组越界或者除0,注意要防止代码被优化掉,如果异常代码被优化掉,则没有任何反应。