From 4f321973ac0e6140a4d48d62691c8aa41a575e41 Mon Sep 17 00:00:00 2001 From: matcool <26722564+matcool@users.noreply.github.com> Date: Sat, 20 Jul 2024 16:28:03 -0300 Subject: [PATCH] implement custom window for showing crashlog instead of messagebox very cool :-) --- loader/CMakeLists.txt | 4 +- loader/src/internal/crashlog.cpp | 8 +- loader/src/internal/crashlog.hpp | 2 + loader/src/platform/windows/crashlog.cpp | 12 +- .../src/platform/windows/crashlogWindow.cpp | 208 ++++++++++++++++++ 5 files changed, 230 insertions(+), 4 deletions(-) create mode 100644 loader/src/platform/windows/crashlogWindow.cpp diff --git a/loader/CMakeLists.txt b/loader/CMakeLists.txt index 69e99005..9595bbc6 100644 --- a/loader/CMakeLists.txt +++ b/loader/CMakeLists.txt @@ -335,7 +335,9 @@ if (APPLE) elseif (WIN32) add_subdirectory(launcher/windows) - target_link_libraries(${PROJECT_NAME} dbghelp) + target_link_libraries(${PROJECT_NAME} dbghelp comctl32 uxtheme Winmm) + + target_compile_definitions(${PROJECT_NAME} PUBLIC ISOLATION_AWARE_ENABLED=1) if (MSVC) # disable warnings about CCNode::setID diff --git a/loader/src/internal/crashlog.cpp b/loader/src/internal/crashlog.cpp index 279a73d8..359fa926 100644 --- a/loader/src/internal/crashlog.cpp +++ b/loader/src/internal/crashlog.cpp @@ -53,6 +53,11 @@ void crashlog::printMods(std::stringstream& stream) { } std::string crashlog::writeCrashlog(geode::Mod* faultyMod, std::string const& info, std::string const& stacktrace, std::string const& registers) { + std::filesystem::path outPath; + return writeCrashlog(faultyMod, info, stacktrace, registers, outPath); +} + +std::string crashlog::writeCrashlog(geode::Mod* faultyMod, std::string const& info, std::string const& stacktrace, std::string const& registers, std::filesystem::path& outPath) { // make sure crashlog directory exists (void)utils::file::createDirectoryAll(crashlog::getCrashLogDirectory()); @@ -94,9 +99,10 @@ std::string crashlog::writeCrashlog(geode::Mod* faultyMod, std::string const& in printMods(file); // save actual file + outPath = crashlog::getCrashLogDirectory() / (getDateString(true) + ".log"); std::ofstream actualFile; actualFile.open( - crashlog::getCrashLogDirectory() / (getDateString(true) + ".log"), std::ios::app + outPath, std::ios::app ); actualFile << file.rdbuf() << std::flush; actualFile.close(); diff --git a/loader/src/internal/crashlog.hpp b/loader/src/internal/crashlog.hpp index 7f1c9d72..eb907b78 100644 --- a/loader/src/internal/crashlog.hpp +++ b/loader/src/internal/crashlog.hpp @@ -33,6 +33,8 @@ namespace crashlog { std::string GEODE_DLL writeCrashlog(geode::Mod* faultyMod, std::string const& info, std::string const& stacktrace, std::string const& registers); + std::string writeCrashlog(geode::Mod* faultyMod, std::string const& info, std::string const& stacktrace, std::string const& registers, std::filesystem::path& outCrashlogPath); + std::string getDateString(bool filesafe); void GEODE_DLL printGeodeInfo(std::stringstream& stream); diff --git a/loader/src/platform/windows/crashlog.cpp b/loader/src/platform/windows/crashlog.cpp index 53006c54..d2cd97ff 100644 --- a/loader/src/platform/windows/crashlog.cpp +++ b/loader/src/platform/windows/crashlog.cpp @@ -464,6 +464,7 @@ static std::string getInfo(LPEXCEPTION_POINTERS info, Mod* faultyMod, Mod* suspe static void handleException(LPEXCEPTION_POINTERS info) { std::string text; + std::filesystem::path crashlogPath; // calling SymInitialize from multiple threads can have unexpected behavior, so synchronize this part static std::mutex symMutex; @@ -492,7 +493,8 @@ static void handleException(LPEXCEPTION_POINTERS info) { faultyMod, crashInfo, stacktrace, - getRegisters(info->ContextRecord) + getRegisters(info->ContextRecord), + crashlogPath ); if (g_symbolsInitialized) { @@ -500,7 +502,13 @@ static void handleException(LPEXCEPTION_POINTERS info) { } } - MessageBoxA(nullptr, text.c_str(), "Geometry Dash Crashed", MB_ICONERROR); + // defined in crashlogWindow.cpp + extern bool showCustomCrashlogWindow(std::string text, std::filesystem::path const& crashlogPath); + + if (!showCustomCrashlogWindow(text, crashlogPath)) { + // if the window fails to show, we show a message box instead + MessageBoxA(nullptr, text.c_str(), "Geometry Dash Crashed", MB_ICONERROR); + } } static LONG WINAPI exceptionHandler(LPEXCEPTION_POINTERS info) { diff --git a/loader/src/platform/windows/crashlogWindow.cpp b/loader/src/platform/windows/crashlogWindow.cpp new file mode 100644 index 00000000..34049348 --- /dev/null +++ b/loader/src/platform/windows/crashlogWindow.cpp @@ -0,0 +1,208 @@ +#define ISOLATION_AWARE_ENABLED 1 + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +// comctl32 v6 +#pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") + +enum { + ID_CRASHLOG_TEXT = 101, + ID_BUTTON_CLOSE = 102, + ID_BUTTON_OPEN_FOLDER = 103, + ID_BUTTON_COPY_CLIPBOARD = 104, +}; +#define TO_HMENU(x) reinterpret_cast(static_cast(x)) + +namespace layout { + static constexpr int CRASHLOG_FONT_SIZE = 16; + static constexpr int BUTTON_HEIGHT = 30; + static constexpr int BUTTON_WIDTH = 120; + static constexpr int BUTTON_SPACING = 10; + + static constexpr int PADDING = 10; +} + +// dont judge +std::filesystem::path g_crashlogPath; +std::string g_crashlogText; + +LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { + switch (msg) { + case WM_CLOSE: + DestroyWindow(hwnd); + break; + case WM_DESTROY: + PostQuitMessage(0); + break; + + case WM_CREATE: { + { + // center the window + RECT desktopRect; + GetClientRect(GetDesktopWindow(), &desktopRect); + + RECT windowRect; + GetWindowRect(hwnd, &windowRect); + + auto x = desktopRect.right / 2 - (windowRect.right - windowRect.left) / 2; + auto y = desktopRect.bottom / 2 - (windowRect.bottom - windowRect.top) / 2; + + SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOZORDER | SWP_NOSIZE); + } + + auto monoFont = CreateFontA(layout::CRASHLOG_FONT_SIZE, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, ANSI_CHARSET, + OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, + DEFAULT_PITCH | FF_DONTCARE, TEXT("Consolas")); + auto guiFont = static_cast(GetStockObject(DEFAULT_GUI_FONT)); + + auto handleText = CreateWindowA( + "EDIT", "Crashlog text goes here", WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_LEFT | ES_MULTILINE | ES_READONLY | ES_AUTOVSCROLL | WS_BORDER, + 0, 0, 100, 100, + hwnd, TO_HMENU(ID_CRASHLOG_TEXT), NULL, NULL + ); + SendMessage(handleText, WM_SETFONT, WPARAM(monoFont), TRUE); + // does nothing :( + Edit_SetEndOfLine(handleText, EC_ENDOFLINE_LF); + + auto button = CreateWindowA( + "BUTTON", "Close", + WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, + 0, 0, layout::BUTTON_WIDTH, layout::BUTTON_HEIGHT, + hwnd, TO_HMENU(ID_BUTTON_CLOSE), NULL, NULL + ); + SendMessage(button, WM_SETFONT, WPARAM(guiFont), TRUE); + + button = CreateWindowA( + "BUTTON", "Open crashlog folder", + WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, + 0, 0, layout::BUTTON_WIDTH, layout::BUTTON_HEIGHT, + hwnd, TO_HMENU(ID_BUTTON_OPEN_FOLDER), NULL, NULL + ); + SendMessage(button, WM_SETFONT, WPARAM(guiFont), TRUE); + + button = CreateWindowA( + "BUTTON", "Copy to clipboard", + WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, + 0, 0, layout::BUTTON_WIDTH, layout::BUTTON_HEIGHT, + hwnd, TO_HMENU(ID_BUTTON_COPY_CLIPBOARD), NULL, NULL + ); + SendMessage(button, WM_SETFONT, WPARAM(guiFont), TRUE); + } break; + + case WM_SIZE: { + RECT clientRect; + GetClientRect(hwnd, &clientRect); + + SetWindowPos( + GetDlgItem(hwnd, ID_CRASHLOG_TEXT), NULL, + layout::PADDING, layout::PADDING, + clientRect.right - layout::PADDING * 2, clientRect.bottom - layout::BUTTON_HEIGHT - layout::PADDING * 3, + SWP_NOZORDER + ); + + auto buttonY = clientRect.bottom - layout::BUTTON_HEIGHT - layout::PADDING; + SetWindowPos( + GetDlgItem(hwnd, ID_BUTTON_COPY_CLIPBOARD), NULL, + layout::PADDING, buttonY, + 0, 0, + SWP_NOZORDER | SWP_NOSIZE + ); + SetWindowPos( + GetDlgItem(hwnd, ID_BUTTON_CLOSE), NULL, + clientRect.right - layout::BUTTON_WIDTH - layout::PADDING, buttonY, + 0, 0, + SWP_NOZORDER | SWP_NOSIZE + ); + SetWindowPos( + GetDlgItem(hwnd, ID_BUTTON_OPEN_FOLDER), NULL, + clientRect.right - layout::BUTTON_WIDTH * 2 - layout::BUTTON_SPACING - layout::PADDING, buttonY, + 0, 0, + SWP_NOZORDER | SWP_NOSIZE + ); + } break; + + case WM_CTLCOLORSTATIC: { + return (LRESULT)(COLOR_WINDOWFRAME); + } break; + + case WM_COMMAND: { + auto id = LOWORD(wParam); + if (id == ID_BUTTON_CLOSE) { + DestroyWindow(hwnd); + } else if (id == ID_BUTTON_OPEN_FOLDER) { + geode::utils::file::openFolder(g_crashlogPath); + } else if (id == ID_BUTTON_COPY_CLIPBOARD) { + geode::utils::clipboard::write(g_crashlogText); + } + } break; + + default: + return DefWindowProc(hwnd, msg, wParam, lParam); + } + return 0; +} + +bool showCustomCrashlogWindow(std::string text, std::filesystem::path const& crashlogPath) { + static constexpr auto WINDOW_CLASS_NAME = "GeodeCrashHandlerWindow"; + + g_crashlogPath = crashlogPath; + g_crashlogText = text; + + // i cant get the edit control to use LF, so just replace them myself :-) + for (int i = 0; i < text.size(); ++i) { + auto c = text[i]; + if (c == '\n') { + text.insert(text.begin() + i, '\r'); + ++i; + } + } + + WNDCLASS wc = {0}; + wc.lpfnWndProc = &WndProc; + wc.hInstance = GetModuleHandleA(NULL); + wc.lpszClassName = WINDOW_CLASS_NAME; + + wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)COLOR_WINDOW; + + if (!RegisterClass(&wc)) { + return false; + } + + auto hwnd = CreateWindowExA( + 0, + WINDOW_CLASS_NAME, + "Geode Crash Handler", + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, + NULL, NULL, wc.hInstance, NULL + ); + + if (hwnd == NULL) { + return false; + } + + SetWindowTextA(GetDlgItem(hwnd, ID_CRASHLOG_TEXT), text.c_str()); + + ShowWindow(hwnd, SW_SHOWNORMAL); + PlaySound((LPCTSTR)SND_ALIAS_SYSTEMDEFAULT, NULL, SND_ASYNC | SND_ALIAS_ID); + UpdateWindow(hwnd); + + MSG message; + while (GetMessage(&message, NULL, 0, 0) > 0) { + TranslateMessage(&message); + DispatchMessage(&message); + } + return true; +}