实际上就是调用系统函数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();
}
有问题请指正。