捕获异常并自动保存dump

SetUnhandledExceptionFilter 捕获的异常有限,比如ctr异常、栈溢出的异常就无法捕获。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/************************************************************************/
/* 之所以应用程序捕获不到栈溢出异常,原因是因为新版本的CRT实现在异常处理中强制删除所有应用程序先前设置的捕获函数,如下所示:

__crtUnhandledException()
{
SetUnhandledExceptionFilter(NULL);

UnhandledExceptionFilter(&ExceptionPointers);
}
解决方法是拦截CRT调用SetUnhandledExceptionFilter函数,使之无效。在X86平台下,可以使用以下代码 * /
/************************************************************************/

void DisableSetUnhandledExceptionFilter()
{
void *addr = (void*)GetProcAddress(LoadLibrary(_T("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;

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

void WriteMiniDMP(struct _EXCEPTION_POINTERS *pExp)
{
CString strDumpFile;
TCHAR szFilePath[MAX_PATH];
GetModuleFileName(NULL, szFilePath, MAX_PATH);
*strrchr(szFilePath, '\\') = 0;

SYSTEMTIME stTime;
GetLocalTime(&stTime);

// 保存fulldump
{
strDumpFile.Format("%s\\[%02d-%02d %02d-%02d-%02d]full.dmp", szFilePath, stTime.wMonth, stTime.wDay, stTime.wHour, stTime.wMinute, stTime.wSecond);
HANDLE hFile = CreateFile(strDumpFile, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
MINIDUMP_EXCEPTION_INFORMATION ExInfo;
ExInfo.ThreadId = ::GetCurrentThreadId();
ExInfo.ExceptionPointers = pExp;
ExInfo.ClientPointers = NULL;
// write the dump
(void)MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, (MINIDUMP_TYPE)0x9b67, &ExInfo, NULL, NULL);
CloseHandle(hFile);
}
}
}

LONG WINAPI ExpFilter(struct _EXCEPTION_POINTERS *pExp)
{
static bool flag = false;
if (!flag){ // 防止多个线程同时写dump导致系统函数死锁
flag = true;
WriteMiniDMP(pExp);
flag = false;
}
return EXCEPTION_EXECUTE_HANDLER;
}

int main(int argc, char* argv[])
{
::SetUnhandledExceptionFilter(ExpFilter);
//捕获crt的栈溢出异常
if ("10" != getOSName()) // win10 系统不生效
{
DisableSetUnhandledExceptionFilter();
}
........
}

如上基本可以捕获大部分异常