本文包含:
信号处理器、STL标准库异常处理、STL未处理器异常处理、SEH结构化未处理异常处理、C++ 未定义虚函数异常处理、C/C++ 内存分配异常处理等。
设置异常处理器:
// Windows platforms need to mount unhandled exception handlers so that they can print dump debug files for app crashes.
#if defined(_DEBUG)
::_CrtSetDbgFlag(_CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF);
#endif
::_set_abort_behavior(_CALL_REPORTFAULT, _CALL_REPORTFAULT);
::_set_purecall_handler(Crt_HandlePureVirtualCall);
::_set_new_handler(Crt_NewHandler); /* std::set_new_handler(...) */
#if _MSC_VER >= 1400
::_set_invalid_parameter_handler(Crt_InvalidParameterHandler);
#endif
::signal(SIGABRT, Crt_SigabrtHandler);
::signal(SIGINT, Crt_SigabrtHandler);
::signal(SIGTERM, Crt_SigabrtHandler);
::signal(SIGILL, Crt_SigabrtHandler);
::set_terminate(Crt_TerminateHandler);
::set_unexpected(Crt_UnexpectedHandler);
::SetUnhandledExceptionFilter(Seh_UnhandledExceptionFilter);
异常处理器实现:
static ppp::string Seh_NewDumpFileName() noexcept
{
ppp::string path = ppp::GetExecutionFileName();
std::size_t index = path.rfind(".");
if (index != ppp::string::npos)
{
path = path.substr(0, index);
}
struct tm tm_;
time_t datetime = time(NULL);
localtime_s(&tm_, &datetime);
char sz[1000];
sprintf_s(sz, sizeof(sz), "%04d%02d%02d-%02d%02d%02d", 1900 + tm_.tm_year, 1 + tm_.tm_mon, tm_.tm_mday, tm_.tm_hour, tm_.tm_min, tm_.tm_sec);
path = path + "-" + sz + ".dmp";
path = "./" + path;
path = ppp::io::File::RewritePath(path.data());
path = ppp::io::File::GetFullPath(path.data());
return path;
}
static LONG WINAPI Seh_UnhandledExceptionFilter(EXCEPTION_POINTERS* exceptionInfo) noexcept
{
// Give user code a chance to approve or prevent writing a minidump. If the
// filter returns false, don't handle the exception at all. If this method
// was called as a result of an exception, returning false will cause
// HandleException to call any previous handler or return
// EXCEPTION_CONTINUE_SEARCH on the exception thread, allowing it to appear
// as though this handler were not present at all.
HANDLE hFile = CreateFileA(Seh_NewDumpFileName().data(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
MINIDUMP_EXCEPTION_INFORMATION exceptionParam;
exceptionParam.ThreadId = GetCurrentThreadId();
exceptionParam.ExceptionPointers = exceptionInfo;
exceptionParam.ClientPointers = TRUE;
MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpWithFullMemory, &exceptionParam, NULL, NULL);
CloseHandle(hFile);
}
// The handler either took care of the invalid parameter problem itself,
// or passed it on to another handler. "Swallow" it by exiting, paralleling
// the behavior of "swallowing" exceptions.
exit(-1); /* abort(); */
return EXCEPTION_EXECUTE_HANDLER;
}
#if _MSC_VER >= 1400
// https://chromium.googlesource.com/breakpad/breakpad/src/+/master/client/windows/handler/exception_handler.cc
static void __CRTDECL Crt_InvalidParameterHandler(const wchar_t* expression, const wchar_t* function, const wchar_t* file, unsigned int line, uintptr_t pReserved) noexcept
{
std::wcerr << L"Invalid parameter detected:" << std::endl;
std::wcerr << L"Expression: " << expression << std::endl;
std::wcerr << L"Function: " << function << std::endl;
std::wcerr << L"File: " << file << std::endl;
std::wcerr << L"Line: " << line << std::endl;
_CrtDumpMemoryLeaks();
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG);
_CrtMemDumpAllObjectsSince(NULL);
_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
// Make up an exception record for the current thread and CPU context
// to make it possible for the crash processor to classify these
// as do regular crashes, and to make it humane for developers to
// analyze them.
EXCEPTION_RECORD exception_record = {};
CONTEXT exception_context = {};
EXCEPTION_POINTERS exception_ptrs = { &exception_record, &exception_context };
::RtlCaptureContext(&exception_context);
exception_record.ExceptionCode = STATUS_INVALID_PARAMETER;
// We store pointers to the the expression and function strings,
// and the line as exception parameters to make them easy to
// access by the developer on the far side.
exception_record.NumberParameters = 4;
exception_record.ExceptionInformation[0] = reinterpret_cast<ULONG_PTR>(expression);
exception_record.ExceptionInformation[1] = reinterpret_cast<ULONG_PTR>(file);
exception_record.ExceptionInformation[2] = line;
exception_record.ExceptionInformation[3] = reinterpret_cast<ULONG_PTR>(function);
// Deliver exceptions to unhandled exception handler.
Seh_UnhandledExceptionFilter(&exception_ptrs);
}
#endif
static int Seh_NoncontinuableException() noexcept
{
// Make up an exception record for the current thread and CPU context
// to make it possible for the crash processor to classify these
// as do regular crashes, and to make it humane for developers to
// analyze them.
EXCEPTION_RECORD exception_record = {};
CONTEXT exception_context = {};
EXCEPTION_POINTERS exception_ptrs = { &exception_record, &exception_context };
::RtlCaptureContext(&exception_context);
exception_record.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION;
// We store pointers to the the expression and function strings,
// and the line as exception parameters to make them easy to
// access by the developer on the far side.
exception_record.NumberParameters = 3;
exception_record.ExceptionInformation[0] = NULL;
exception_record.ExceptionInformation[1] = NULL;
exception_record.ExceptionInformation[2] = 0;
// Deliver exceptions to unhandled exception handler.
return Seh_UnhandledExceptionFilter(&exception_ptrs);
}
static int __CRTDECL Crt_NewHandler(size_t) noexcept
{
return Seh_NoncontinuableException();
}
static void __CRTDECL Crt_HandlePureVirtualCall() noexcept
{
Seh_NoncontinuableException();
}
static void __CRTDECL Crt_TerminateHandler() noexcept
{
Seh_NoncontinuableException();
}
static void __CRTDECL Crt_UnexpectedHandler() noexcept
{
Seh_NoncontinuableException();
}
static void __CRTDECL Crt_SigabrtHandler(int) noexcept
{
Seh_NoncontinuableException();
}