老生常谈:让软件留下临终遗言并优雅地关闭

实际上就是调用系统函数SetUnhandledExceptionFilter设置顶层异常捕获函数,并且利用MiniDumpWriteDump函数将异常堆栈输出到文件中,方便问题的定位。

参考代码如下:

#include <windows.h>
#include <imagehlp.h>
#include <Strsafe.h>

#pragma comment(lib, "dbghelp")

static LONG WINAPI __XUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *pExceptionPointers)   
{
	SetErrorMode( SEM_NOGPFAULTERRORBOX );  

	WCHAR szBuild[64+1];
	StringCchPrintfW(szBuild, 64, L"Build: %s %s", __DATE__, __TIME__);
	WCHAR szError[MAX_PATH*2+1];
	WCHAR szModuleName[MAX_PATH] = L"";   
	GetModuleFileNameW(NULL, szModuleName, ARRAYSIZE(szModuleName));   
	StringCchPrintfW(szError, MAX_PATH*2, L"%s %d , %d ,%d.", szModuleName,pExceptionPointers->ExceptionRecord->ExceptionCode, pExceptionPointers->ExceptionRecord->ExceptionFlags, pExceptionPointers->ExceptionRecord->ExceptionAddress);   

	BOOL bMiniDumpSuccessful;	
	WCHAR szFileName[MAX_PATH];
	DWORD dwBufferSize = MAX_PATH;
	HANDLE hDumpFile;   
	SYSTEMTIME stLocalTime;   
	MINIDUMP_EXCEPTION_INFORMATION ExpParam;
	GetLocalTime( &stLocalTime ); 

	WCHAR* p = wcsrchr(szModuleName, L'\\');
	if( p != NULL )
	{
		*p = L'\0';
	}

	StringCchPrintfW(szFileName, MAX_PATH, L"%s\\dmp", szModuleName);
	CreateDirectoryW(szFileName, NULL);

	if ( p == NULL )
	{
		StringCchPrintfW( szFileName, MAX_PATH, L"%s\\dmp\\%04d%02d%02d-%02d%02d%02d-%ld-%ld.dmp",
			szModuleName,
			stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,
			stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond,    
			GetCurrentProcessId(), GetCurrentThreadId());
	}
	else
	{
		StringCchPrintfW( szFileName, MAX_PATH, L"%s\\dmp\\%s-%04d%02d%02d-%02d%02d%02d-%ld-%ld.dmp",
			szModuleName, ++p,
			stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,
			stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond,    
			GetCurrentProcessId(), GetCurrentThreadId());
	}
	
	hDumpFile = CreateFileW(szFileName, GENERIC_READ|GENERIC_WRITE,    
		FILE_SHARE_WRITE|FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0);

	if ( hDumpFile == INVALID_HANDLE_VALUE )
	{
		return EXCEPTION_EXECUTE_HANDLER;
	}

	MINIDUMP_USER_STREAM UserStream[2];   
	MINIDUMP_USER_STREAM_INFORMATION UserInfo;   
	UserInfo.UserStreamCount = 1;   
	UserInfo.UserStreamArray = UserStream;   
	UserStream[0].Type = CommentStreamW;   
	UserStream[0].BufferSize = wcslen(szBuild)*sizeof(WCHAR);   
	UserStream[0].Buffer = szBuild;   
	UserStream[1].Type = CommentStreamW;   
	UserStream[1].BufferSize = wcslen(szError)*sizeof(WCHAR);   
	UserStream[1].Buffer = szError;

	ExpParam.ThreadId = GetCurrentThreadId();   
	ExpParam.ExceptionPointers = pExceptionPointers;   
	ExpParam.ClientPointers = TRUE;   

	MINIDUMP_TYPE MiniDumpWithDataSegs = MINIDUMP_TYPE(MiniDumpNormal|MiniDumpWithHandleData 
		| MiniDumpWithUnloadedModules    
		| MiniDumpWithIndirectlyReferencedMemory    
		| MiniDumpScanMemory    
		| MiniDumpWithProcessThreadData    
		| MiniDumpWithThreadInfo);

	bMiniDumpSuccessful = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),    
		hDumpFile, MiniDumpWithDataSegs, &ExpParam, NULL, NULL);   

	return EXCEPTION_EXECUTE_HANDLER; //关闭程序   
}

void DisableSetUnhandledExceptionFilter()
{
	// 
	// 修改SetUnhandledExceptionFilter函数的汇编代码
	// 进入后直接跳转返回
	//
	void* addr = (void*)GetProcAddress(LoadLibrary("kernel32.dll"),
		"SetUnhandledExceptionFilter");

	if (addr) 
	{
		unsigned char code[16];
		int size = 0;

		code[size++] = 0x33;
		code[size++] = 0xC0;
		code[size++] = 0xC2;
		code[size++] = 0x04;
		code[size++] = 0x00;

		DWORD dwOldFlag, dwTempFlag;
		VirtualProtect(addr, size, PAGE_READWRITE, &dwOldFlag);
		WriteProcessMemory(GetCurrentProcess(), addr, code, size, NULL);
		VirtualProtect(addr, size, dwOldFlag, &dwTempFlag);
	}
}

void installExceptionHandler()
{
	SetUnhandledExceptionFilter(__XUnhandledExceptionFilter);

	//
	// 从 VC++2005 开始出于安全因素微软改变了 CRT 的行为.
	// 某些情况下CRT会调用SetUnhandledExceptionFilter(NULL)清除所有异常处理函数
	// 导致不会通知被注册的Unhandled Exception Filter.
	// 为此在设置完自己的异常处理函数后,让SetUnhandledExceptionFilter函数无效。
	//
	DisableSetUnhandledExceptionFilter();
}
有问题请指正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值