/* * (C) 2017 see Authors.txt * * This file is part of MPC-HC. * * MPC-HC is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * MPC-HC is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "stdafx.h" #include "ExceptionHandler.h" #include #include #ifndef _DEBUG LPCWSTR GetExceptionName(DWORD code) { switch (code) { case EXCEPTION_ACCESS_VIOLATION: return _T("ACCESS VIOLATION"); case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return _T("ARRAY BOUNDS EXCEEDED"); case EXCEPTION_BREAKPOINT: return _T("BREAKPOINT"); case EXCEPTION_DATATYPE_MISALIGNMENT: return _T("DATATYPE MISALIGNMENT"); case EXCEPTION_FLT_DENORMAL_OPERAND: return _T("FLT DENORMAL OPERAND"); case EXCEPTION_FLT_DIVIDE_BY_ZERO: return _T("FLT DIVIDE BY ZERO"); case EXCEPTION_FLT_INEXACT_RESULT: return _T("FLT INEXACT RESULT"); case EXCEPTION_FLT_INVALID_OPERATION: return _T("FLT INVALID OPERATION"); case EXCEPTION_FLT_OVERFLOW: return _T("FLT OVERFLOW"); case EXCEPTION_FLT_STACK_CHECK: return _T("FLT STACK CHECK"); case EXCEPTION_FLT_UNDERFLOW: return _T("FLT UNDERFLOW"); case EXCEPTION_GUARD_PAGE: return _T("GUARD PAGE"); case EXCEPTION_ILLEGAL_INSTRUCTION: return _T("ILLEGAL_INSTRUCTION"); case EXCEPTION_IN_PAGE_ERROR: return _T("IN PAGE ERROR"); case EXCEPTION_INT_DIVIDE_BY_ZERO: return _T("INT DIVIDE BY ZERO"); case EXCEPTION_INT_OVERFLOW: return _T("INT OVERFLOW"); case EXCEPTION_INVALID_DISPOSITION: return _T("INVALID DISPOSITION"); case EXCEPTION_INVALID_HANDLE: return _T("INVALID HANDLE"); case EXCEPTION_NONCONTINUABLE_EXCEPTION: return _T("NONCONTINUABLE EXCEPTION"); case EXCEPTION_PRIV_INSTRUCTION: return _T("PRIV INSTRUCTION"); case EXCEPTION_SINGLE_STEP: return _T("SINGLE STEP"); case EXCEPTION_STACK_OVERFLOW: return _T("STACK OVERFLOW"); case 0xE06D7363: return _T("UNDEFINED C++ EXCEPTION"); default: return _T("[UNKNOWN]"); } } HMODULE GetExceptionModule(LPVOID address, LPWSTR moduleName) { HMODULE moduleList[1024]; DWORD sizeNeeded = 0; if (!EnumProcessModules(GetCurrentProcess(), moduleList, sizeof(moduleList), &sizeNeeded) || sizeNeeded > sizeof(moduleList)) { return nullptr; } int curModule = -1; for (DWORD i = 0; i < (sizeNeeded / sizeof(HMODULE)); ++i) { if (moduleList[i] < address) { if (curModule == -1) { curModule = i; } else { if (moduleList[curModule] < moduleList[i]) { curModule = i; } } } } if (curModule == -1) { return nullptr; } if (!GetModuleFileName(moduleList[curModule], moduleName, MAX_PATH)) { return nullptr; } return moduleList[curModule]; } void HandleCommonException(LPEXCEPTION_POINTERS exceptionInfo) { wchar_t message[MAX_PATH + 255]; wchar_t module[MAX_PATH]; wchar_t* moduleName = nullptr; if (GetExceptionModule(exceptionInfo->ExceptionRecord->ExceptionAddress, module)) { moduleName = module; } else { moduleName = _T("[UNKNOWN]"); } uint64_t codeBase = (uint64_t)GetModuleHandle(NULL); uint64_t offset = (uint64_t)exceptionInfo->ExceptionRecord->ExceptionAddress - codeBase; swprintf_s(message, _countof(message), _T(\ "An error has occurred. MPC-HC will close now.\n\n"\ "Exception:\n%s\n\n"\ "Crashing module:\n%s\n"\ "Offset: 0x%I64X, Codebase: 0x%I64X\n\n"), GetExceptionName(exceptionInfo->ExceptionRecord->ExceptionCode), moduleName, offset, codeBase); MessageBox(AfxGetApp()->GetMainWnd()->GetSafeHwnd(), message, _T("Unexpected error"), MB_OK | MB_TOPMOST | MB_SETFOREGROUND | MB_SYSTEMMODAL); } void HandleAccessViolation(LPEXCEPTION_POINTERS exceptionInfo) { wchar_t message[MAX_PATH + 255]; wchar_t module[MAX_PATH]; wchar_t* moduleName = NULL; if (GetExceptionModule(exceptionInfo->ExceptionRecord->ExceptionAddress, module)) { moduleName = module; } else { moduleName = _T("[UNKNOWN]"); } uint64_t codeBase = (uint64_t)GetModuleHandle(NULL); uint64_t offset = (uint64_t)exceptionInfo->ExceptionRecord->ExceptionAddress - codeBase; wchar_t* accessType = NULL; switch (exceptionInfo->ExceptionRecord->ExceptionInformation[0]) { case 0: accessType = _T("read"); break; case 1: accessType = _T("write"); break; case 2: accessType = _T("execute"); break; default: accessType = _T("[UNKNOWN]"); break; } swprintf_s(message, _countof(message), _T(\ "An error has occurred. MPC-HC will close now.\n\n"\ "Exception:\n%s\n\n"\ "Crashing module:\n%s\n"\ "Offset: 0x%I64X, Codebase: 0x%I64X\n"\ "The thread %u tried to %s memory at address 0x%IX\n\n"), GetExceptionName(exceptionInfo->ExceptionRecord->ExceptionCode), moduleName, offset, codeBase, GetCurrentThreadId(), accessType, exceptionInfo->ExceptionRecord->ExceptionInformation[1]); MessageBox(AfxGetApp()->GetMainWnd()->GetSafeHwnd(), message, _T("Unexpected error"), MB_OK | MB_TOPMOST | MB_SETFOREGROUND | MB_SYSTEMMODAL); } LONG WINAPI UnhandledException(LPEXCEPTION_POINTERS exceptionInfo) { switch (exceptionInfo->ExceptionRecord->ExceptionCode) { case EXCEPTION_ACCESS_VIOLATION: HandleAccessViolation(exceptionInfo); break; default: HandleCommonException(exceptionInfo); break; } return EXCEPTION_EXECUTE_HANDLER; } #endif void MPCExceptionHandler::Enable() { #ifndef _DEBUG SetUnhandledExceptionFilter(UnhandledException); #endif }; void MPCExceptionHandler::Disable() { #ifndef _DEBUG SetUnhandledExceptionFilter(nullptr); #endif };