背景
windows下开发,设置了SetUnhandledExceptionFilter,但是仍然无法捕获异常。百度后,知道这里设置后可能会被重置为NULL。所以此次设置好后,还需要保护一下。
方法
SetUnhandledExceptionFilter()之后,再调用DisableSetUnhandledExceptionFilter即可。
注意,DisableSetUnhandledExceptionFilter()中,网上大多是仅支持x86的。此处是x64的方法。
代码
#include "MainWidget.h"
#include <QWindow>
#include <iostream>
#include <stdint.h>
#include "JsonHelper.h"
#include <windows.h>
#include <signal.h>
#include <new.h>
#include <limits>
#include <exception>
#include <DbgHelp.h>
#include <minwinbase.h>
#pragma comment(lib,"Dbghelp.lib")
#pragma comment(lib,"User32.lib")
static int GenerateDump(EXCEPTION_POINTERS* exceptionPointers, const std::string& path)
{
HANDLE hFile = ::CreateFileA(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE != hFile)
{
MINIDUMP_EXCEPTION_INFORMATION minidumpExceptionInformation;
minidumpExceptionInformation.ThreadId = GetCurrentThreadId();
minidumpExceptionInformation.ExceptionPointers = exceptionPointers;
minidumpExceptionInformation.ClientPointers = TRUE;
bool isMiniDumpGenerated = MiniDumpWriteDump(
GetCurrentProcess(),
GetCurrentProcessId(),
hFile,
MINIDUMP_TYPE::MiniDumpNormal,
&minidumpExceptionInformation,
nullptr,
nullptr);
CloseHandle(hFile);
if (!isMiniDumpGenerated)
{
printf("MiniDumpWriteDump failed\n");
}
}
else
{
printf("Failed to create dump file\n");
}
return EXCEPTION_EXECUTE_HANDLER;
}
void Snapshot(const std::string& path)
{
__try
{
//通过触发异常获取堆栈
RaiseException(0xE0000001, 0, 0, 0);
}
__except (GenerateDump(GetExceptionInformation(), path)) {}
}
LONG UnhandledStructuredException(struct _EXCEPTION_POINTERS *excp)
{
GenerateDump(excp, ".\\test.dmp");
std::stringstream ss;
ss << "IN UnhandledStructuredException.";
ss << " ExceptionCode: 0x" << std::hex << excp->ExceptionRecord->ExceptionCode;
ss << " ExceptionFlags:" << excp->ExceptionRecord->ExceptionFlags;
system(("cmd /K echo " + ss.str()).c_str());
exit(EXIT_FAILURE);
}
void PureCallHandler(void)
{
Snapshot(".\\test.dmp");
exit(EXIT_FAILURE);
}
int NewHandler(size_t id) {
Snapshot(".\\test.dmp");
exit(EXIT_FAILURE);
return 0;
}
void InvalidParameterHandler(const wchar_t* expression,
const wchar_t* function,
const wchar_t* file,
unsigned int line,
uintptr_t pReserved)
{
Snapshot(".\\test.dmp");
exit(EXIT_FAILURE);
}
void SigabrtHandler(int id) {
Snapshot(".\\test.dmp");
exit(EXIT_FAILURE);
}
void SigintHandler(int id) {
Snapshot(".\\test.dmp");
exit(EXIT_FAILURE);
}
void SigtermHandler(int id) {
Snapshot(".\\test.dmp");
exit(EXIT_FAILURE);
}
void SigillHandler(int id) {
Snapshot(".\\test.dmp");
exit(EXIT_FAILURE);
}
void TerminateHandler() {
Snapshot(".\\test.dmp");
exit(EXIT_FAILURE);
}
void UnexpectedHandler() {
Snapshot(".\\test.dmp");
exit(EXIT_FAILURE);
}
void InstallUnexceptedExceptionHandler()
{
//SEH(Windows 结构化异常处理),属于Win32 API
::SetUnhandledExceptionFilter(UnhandledStructuredException);
//C 运行时库 (CRT) 异常处理,由 CRT 提供的异常处理机制。
_set_purecall_handler(PureCallHandler);
_set_new_handler(NewHandler);
_set_invalid_parameter_handler(InvalidParameterHandler);
_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
//C 运行时信号处理,由 CRT 提供的信号处理机制。
signal(SIGABRT, SigabrtHandler);
signal(SIGINT, SigintHandler);
signal(SIGTERM, SigtermHandler);
signal(SIGILL, SigillHandler);
//C++ 运行时异常处理,API由标准库提供
set_terminate(TerminateHandler);
set_unexpected(UnexpectedHandler);
}
void DisableSetUnhandlerExcptionFilter()
{
#ifndef _WIN64
system("cmd /K echo The following code only works for x64");
exit(EXIT_FAILURE);
#endif
void* addr = (void*)GetProcAddress(LoadLibrary(L"kernel32.dll"), "SetUnhandledExceptionFilter");
if (addr)
{
unsigned char code[16];
int size = 0;
code[size++] = 0x48;
code[size++] = 0x31;
code[size++] = 0xC0;
code[size++] = 0xC3;
DWORD dwOldFlag, dwTempFlag;
VirtualProtect(addr, size, PAGE_READWRITE, &dwOldFlag);
WriteProcessMemory(GetCurrentProcess(), addr, code, size, NULL);
VirtualProtect(addr, size, dwOldFlag, &dwTempFlag);
}
}
int main(int argc, char *argv[])
{
InstallUnexceptedExceptionHandler();
DisableSetUnhandlerExcptionFilter();
QApplication::setFont(QFont("Arial", 10));
QApplication app(argc, argv);
MainWidget mainUI;
mainUI.showMaximized();
return app.exec();
}
}