diff --git a/DDrawCompat/Config/Config.cpp b/DDrawCompat/Config/Config.cpp index 3dcc8a5..0166824 100644 --- a/DDrawCompat/Config/Config.cpp +++ b/DDrawCompat/Config/Config.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -62,6 +63,7 @@ namespace Config Settings::ConfigTransparency configTransparency; Settings::CpuAffinity cpuAffinity; Settings::CpuAffinityRotation cpuAffinityRotation; + Settings::CrashDump crashDump; Settings::DepthFormat depthFormat; Settings::DisplayAspectRatio displayAspectRatio; Settings::DesktopColorDepth desktopColorDepth; diff --git a/DDrawCompat/Config/Settings/CrashDump.h b/DDrawCompat/Config/Settings/CrashDump.h new file mode 100644 index 0000000..109ad48 --- /dev/null +++ b/DDrawCompat/Config/Settings/CrashDump.h @@ -0,0 +1,36 @@ +#pragma once + +#include + +#include + +namespace Config +{ + namespace Settings + { + class CrashDump : public MappedSetting + { + public: + static const UINT OFF = 0; + + static const UINT MINI = + MiniDumpWithDataSegs | + MiniDumpWithUnloadedModules | + MiniDumpWithProcessThreadData; + + static const UINT FULL = + MiniDumpWithFullMemory | + MiniDumpWithHandleData | + MiniDumpWithUnloadedModules | + MiniDumpWithFullMemoryInfo | + MiniDumpWithThreadInfo | + MiniDumpIgnoreInaccessibleMemory; + + CrashDump() : MappedSetting("CrashDump", "off", { {"off", OFF}, {"mini", MINI}, {"full", FULL} }) + { + } + }; + } + + extern Settings::CrashDump crashDump; +} diff --git a/DDrawCompat/D3dDdi/Hooks.cpp b/DDrawCompat/D3dDdi/Hooks.cpp index 37f9101..23dba9f 100644 --- a/DDrawCompat/D3dDdi/Hooks.cpp +++ b/DDrawCompat/D3dDdi/Hooks.cpp @@ -73,7 +73,7 @@ namespace if ("OpenAdapter" == std::string(lpProcName)) { g_origOpenAdapter = reinterpret_cast( - CALL_ORIG_FUNC(GetProcAddress)(hModule, lpProcName)); + GetProcAddress(hModule, lpProcName)); if (g_origOpenAdapter) { static std::set hookedModules; @@ -89,14 +89,14 @@ namespace else if ("GetPrivateDDITable" == std::string(lpProcName)) { g_origGetPrivateDdiTable = reinterpret_cast( - CALL_ORIG_FUNC(GetProcAddress)(hModule, lpProcName)); + GetProcAddress(hModule, lpProcName)); if (g_origGetPrivateDdiTable) { return reinterpret_cast(&getPrivateDdiTable); } } } - return LOG_RESULT(CALL_ORIG_FUNC(GetProcAddress)(hModule, lpProcName)); + return LOG_RESULT(GetProcAddress(hModule, lpProcName)); } HRESULT APIENTRY kmtPresent(HANDLE hDevice, D3DKMT_PRESENT* pKMTArgs) diff --git a/DDrawCompat/DDrawCompat.vcxproj b/DDrawCompat/DDrawCompat.vcxproj index 6acb57e..f54625c 100644 --- a/DDrawCompat/DDrawCompat.vcxproj +++ b/DDrawCompat/DDrawCompat.vcxproj @@ -174,6 +174,7 @@ + diff --git a/DDrawCompat/DDrawCompat.vcxproj.filters b/DDrawCompat/DDrawCompat.vcxproj.filters index dd51b6f..53e4fb8 100644 --- a/DDrawCompat/DDrawCompat.vcxproj.filters +++ b/DDrawCompat/DDrawCompat.vcxproj.filters @@ -702,6 +702,9 @@ Header Files\Config\Settings + + Header Files\Config\Settings + diff --git a/DDrawCompat/Dll/DllMain.cpp b/DDrawCompat/Dll/DllMain.cpp index 7d4e718..b7083fd 100644 --- a/DDrawCompat/Dll/DllMain.cpp +++ b/DDrawCompat/Dll/DllMain.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include #include @@ -8,8 +9,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -37,6 +40,9 @@ namespace { const DWORD DISABLE_MAX_WINDOWED_MODE = 12; + Compat::CriticalSection g_crashDumpCs; + std::filesystem::path g_crashDumpPath; + template using FuncPtr = Result(WINAPI*)(Params...); @@ -238,6 +244,71 @@ namespace logDpiAwareness(SetProcessDPIAware(), DPI_AWARENESS_CONTEXT_SYSTEM_AWARE, "SetProcessDPIAware"); } + + LPTOP_LEVEL_EXCEPTION_FILTER WINAPI setUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter) + { + LOG_FUNC("SetUnhandledExceptionFilter", Compat::funcPtrToStr(lpTopLevelExceptionFilter)); + LOG_ONCE("Suppressed new unhandled exception filter: " << Compat::funcPtrToStr(lpTopLevelExceptionFilter)); + return LOG_RESULT(nullptr); + } + + LONG WINAPI unhandledExceptionFilter(EXCEPTION_POINTERS* ExceptionInfo) + { + Compat::ScopedCriticalSection lock(g_crashDumpCs); + BOOL result = FALSE; + DWORD error = 0; + HANDLE dumpFile = INVALID_HANDLE_VALUE; + + LOG_INFO << "Terminating application due to unhandled exception: " + << Compat::hex(ExceptionInfo->ExceptionRecord->ExceptionCode); + + const auto writeDump = reinterpret_cast( + GetProcAddress(GetModuleHandle("dbghelp"), "MiniDumpWriteDump")); + + if (writeDump) + { + dumpFile = CreateFileW(g_crashDumpPath.native().c_str(), + GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + if (INVALID_HANDLE_VALUE == dumpFile) + { + error = GetLastError(); + } + else + { + + MINIDUMP_EXCEPTION_INFORMATION mei = {}; + mei.ThreadId = GetCurrentThreadId(); + mei.ExceptionPointers = ExceptionInfo; + result = writeDump(GetCurrentProcess(), GetCurrentProcessId(), dumpFile, + static_cast(Config::crashDump.get()), &mei, nullptr, nullptr); + if (!result) + { + error = GetLastError(); + } + CloseHandle(dumpFile); + } + } + + if (result) + { + LOG_INFO << "Crash dump has been written to: " << g_crashDumpPath.native().c_str(); + } + else if (!writeDump) + { + LOG_INFO << "Failed to load procedure MiniDumpWriteDump to create a crash dump"; + } + else if (INVALID_HANDLE_VALUE == dumpFile) + { + LOG_INFO << "Failed to create crash dump file: " << Compat::hex(error); + } + else + { + LOG_INFO << "Failed to write crash dump: " << Compat::hex(error); + } + + TerminateProcess(GetCurrentProcess(), 0); + return EXCEPTION_CONTINUE_SEARCH; + } } #define LOAD_ORIG_PROC(proc) \ @@ -276,6 +347,25 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) Config::Parser::loadAllConfigFiles(processPath); Compat::Log::initLogging(processPath, Config::logLevel.get()); + if (Config::Settings::CrashDump::OFF != Config::crashDump.get()) + { + g_crashDumpPath = processPath; + if (Compat::isEqual(g_crashDumpPath.extension(), ".exe")) + { + g_crashDumpPath.replace_extension(); + } + g_crashDumpPath.replace_filename(L"DDrawCompat-" + g_crashDumpPath.filename().native()); + g_crashDumpPath += ".dmp"; + + HOOK_FUNCTION(kernel32, SetUnhandledExceptionFilter, setUnhandledExceptionFilter); + LOG_INFO << "Installing unhandled exception filter for automatic crash dumps"; + auto prevFilter = CALL_ORIG_FUNC(SetUnhandledExceptionFilter)(&unhandledExceptionFilter); + if (prevFilter) + { + LOG_INFO << "Replaced previous unhandled exception filter: " << Compat::funcPtrToStr(prevFilter); + } + } + auto systemPath(Compat::getSystemPath()); if (Compat::isEqual(currentDllPath.parent_path(), systemPath)) {