From 5968ea08735e7070798b4f0ace41f3bc074454a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Mon, 16 Mar 2026 17:53:50 -0300 Subject: [PATCH] Greatly improved crash window, now displays the stack-trace and let the user go to report it. --- src/tools/ecode/backward.cpp | 166 ++++++++++++++++++++++++----------- 1 file changed, 117 insertions(+), 49 deletions(-) diff --git a/src/tools/ecode/backward.cpp b/src/tools/ecode/backward.cpp index dc8d97aa7..274cbc6ba 100644 --- a/src/tools/ecode/backward.cpp +++ b/src/tools/ecode/backward.cpp @@ -1,6 +1,7 @@ #include #include #include +#include using namespace EE; using namespace EE::System; @@ -13,6 +14,11 @@ using namespace EE::System; #include +#if EE_PLATFORM == EE_PLATFORM_WIN +#include +#include // Added for ShellExecuteW +#endif + namespace backward { #if EE_PLATFORM == EE_PLATFORM_WIN @@ -159,20 +165,100 @@ class WindowsSignalHandling { cv().wait( lk, [] { return crashed() != crash_status::crashed; } ); } } + // Helper to safely convert UTF-8 strings to std::wstring for the Windows API + static std::wstring utf8_to_wstring( const std::string& str ) { + if ( str.empty() ) + return std::wstring(); + int size_needed = MultiByteToWideChar( CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0 ); + std::wstring wstrTo( size_needed, 0 ); + MultiByteToWideChar( CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], size_needed ); + return wstrTo; + } - static void display_crash_message( const std::string& crashFilePath ) { - std::string crashMsg( - String::format( "ecode has encountered an unrecoverable error and crashed! :'(\n" - "A crash log has been saved at:\n%s\n" - "Please feel free to use this file to report a bug at the ecode Github " - "page so it can be fixed as soon as possible.", - crashFilePath ) ); + // Custom Window Procedure to handle button clicks on our crash dialog + static LRESULT CALLBACK CrashDialogProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ) { + switch ( msg ) { + case WM_COMMAND: { + if ( LOWORD( wParam ) == 1 ) { // Report Button + MessageBoxW( + hwnd, + L"Please quickly verify if this stack trace has already been reported to " + L"avoid duplicate issues.\n\nClick OK to open the issue tracker.", + L"Notice", MB_OK | MB_ICONINFORMATION ); - std::wstring wCrashMsg( crashMsg.begin(), crashMsg.end() ); - std::wstring wTitle = L"ecode crashed!"; + // Replace this URL with the actual ecode issue tracker URL + ShellExecuteW( NULL, L"open", L"https://github.com/SpartanJ/ecode/issues", NULL, + NULL, SW_SHOWNORMAL ); + } else if ( LOWORD( wParam ) == 2 ) { // Close Button + PostQuitMessage( 0 ); + } + break; + } + case WM_DESTROY: + PostQuitMessage( 0 ); + break; + default: + return DefWindowProcW( hwnd, msg, wParam, lParam ); + } + return 0; + } - MessageBoxW( nullptr, wCrashMsg.c_str(), wTitle.c_str(), - MB_OK | MB_ICONERROR | MB_TOPMOST ); + static void display_crash_message( const std::string& crashFilePath, + const std::string& stackTrace ) { + HINSTANCE hInstance = GetModuleHandleW( NULL ); + + // 1. Register a custom Window Class + WNDCLASSEXW wc = { 0 }; + wc.cbSize = sizeof( WNDCLASSEXW ); + wc.lpfnWndProc = CrashDialogProc; + wc.hInstance = hInstance; + wc.hCursor = LoadCursor( NULL, IDC_ARROW ); + wc.hbrBackground = (HBRUSH)( COLOR_BTNFACE + 1 ); + wc.lpszClassName = L"EcodeCrashReporter"; + RegisterClassExW( &wc ); + + // 2. Create the main Dialog Window + HWND hwnd = + CreateWindowExW( WS_EX_TOPMOST, L"EcodeCrashReporter", L"ecode crashed!", + WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_VISIBLE, CW_USEDEFAULT, + CW_USEDEFAULT, 640, 480, NULL, NULL, hInstance, NULL ); + + HFONT hFont = (HFONT)GetStockObject( DEFAULT_GUI_FONT ); + + // 3. Add the Label + std::string crashMsg = "ecode has encountered an unrecoverable error and crashed! :'(\n" + "A crash log has been saved at:\n" + + crashFilePath + "\nError crash log:"; + HWND hLabel = + CreateWindowExW( 0, L"STATIC", utf8_to_wstring( crashMsg ).c_str(), + WS_CHILD | WS_VISIBLE, 10, 10, 600, 56, hwnd, NULL, hInstance, NULL ); + SendMessage( hLabel, WM_SETFONT, (WPARAM)hFont, TRUE ); + + // 4. Add the Text Area (Edit Control) for the Stack Trace + HWND hEdit = CreateWindowExW( 0, L"EDIT", utf8_to_wstring( stackTrace ).c_str(), + WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL | WS_BORDER | + ES_MULTILINE | ES_READONLY | ES_AUTOVSCROLL | ES_AUTOHSCROLL, + 10, 68, 600, 320, hwnd, NULL, hInstance, NULL ); + SendMessage( hEdit, WM_SETFONT, (WPARAM)hFont, TRUE ); + + // 5. Add "Close" Button (Now on the left) + HWND hCloseBtn = + CreateWindowExW( 0, L"BUTTON", L"Close", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 10, 395, + 150, 30, hwnd, (HMENU)2, hInstance, NULL ); + SendMessage( hCloseBtn, WM_SETFONT, (WPARAM)hFont, TRUE ); + + // 6. Add "Report" Button (Now on the right) + HWND hReportBtn = CreateWindowExW( 0, L"BUTTON", L"Report Issue...", + WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 460, 395, 150, 30, + hwnd, (HMENU)1, hInstance, NULL ); + SendMessage( hReportBtn, WM_SETFONT, (WPARAM)hFont, TRUE ); + + // 7. Run the Message Loop for this window + MSG msg; + while ( GetMessage( &msg, NULL, 0, 0 ) > 0 ) { + TranslateMessage( &msg ); + DispatchMessage( &msg ); + } } static void handle_stacktrace( int skip_frames = 0 ) { @@ -190,21 +276,13 @@ class WindowsSignalHandling { appendCrashesPath( crashesPath ); FileSystem::makeDir( crashesPath, true ); } - + std::string dateTimeStr( Sys::getDateTimeStr() ); String::replaceAll( dateTimeStr, " ", "_" ); String::replaceAll( dateTimeStr, ":", "-" ); std::string crashFilePath( String::format( "%sstacktrace_%s.log", crashesPath, dateTimeStr ) ); - std::ofstream outFile( crashFilePath ); - if ( !outFile.is_open() ) { - print_to_console( "Error: Failed to open " + crashFilePath + - " for writing stack trace\n" ); - print_stacktrace_to_console( skip_frames, nullptr ); - return; - } - Printer printer; printer.address = true; printer.object = true; @@ -212,40 +290,30 @@ class WindowsSignalHandling { StackTrace st; st.set_machine_type( printer.resolver().machine_type() ); st.set_thread_handle( thread_handle() ); - st.load_here( 64 + skip_frames, ctx() ); // Increased frame limit + st.load_here( 64 + skip_frames, ctx() ); st.skip_n_firsts( skip_frames ); - printer.print( st, outFile ); - print_stacktrace_to_console( skip_frames, &st ); + // Capture stacktrace to a string in memory + std::ostringstream oss; + printer.print( st, oss ); + std::string stackTraceStr = oss.str(); - outFile.close(); + // Print to console (Keeping your existing logic active) + print_to_console( stackTraceStr ); - display_crash_message( crashFilePath ); - } - - static void print_stacktrace_to_console( int skip_frames, const StackTrace* st = nullptr ) { - if ( AttachConsole( ATTACH_PARENT_PROCESS ) ) { - FILE* console = nullptr; - freopen_s( &console, "CONOUT$", "w", stderr ); - if ( console ) { - Printer printer; - printer.address = true; - printer.object = true; - - if ( st ) { - printer.print( *st, std::cerr ); - } else { - StackTrace new_st; - new_st.set_machine_type( printer.resolver().machine_type() ); - new_st.set_thread_handle( thread_handle() ); - new_st.load_here( 64 + skip_frames, ctx() ); - new_st.skip_n_firsts( skip_frames ); - printer.print( new_st, std::cerr ); - } - fflush( stderr ); - } - FreeConsole(); + // Write string to file + std::ofstream outFile( crashFilePath ); + if ( !outFile.is_open() ) { + print_to_console( "Error: Failed to open " + crashFilePath + + " for writing stack trace\n" + stackTraceStr ); + } else { + outFile << stackTraceStr; + outFile.close(); } + + // Display the custom Windows dialog + String::replaceAll( stackTraceStr, "\n", "\r\n" ); + display_crash_message( crashFilePath, stackTraceStr ); } static void print_to_console( const std::string& message ) {