配合UnhandledExceptionFilterEx()实现崩溃后自动重启

使用函数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,注意要防止代码被优化掉,如果异常代码被优化掉,则没有任何反应。

更多的交流,欢迎加入 暗号271888395

阅读更多

没有更多推荐了,返回首页