diff --git a/lib/libimhex/include/hex/api/event_manager.hpp b/lib/libimhex/include/hex/api/event_manager.hpp index 4847755ed..6a3324e57 100644 --- a/lib/libimhex/include/hex/api/event_manager.hpp +++ b/lib/libimhex/include/hex/api/event_manager.hpp @@ -23,7 +23,7 @@ static void subscribe(void *token, Event::Callback function) { EventManager::subscribe(token, function); } \ static void unsubscribe(const EventManager::EventList::iterator &token) noexcept { EventManager::unsubscribe(token); } \ static void unsubscribe(void *token) noexcept { EventManager::unsubscribe(token); } \ - static void post(auto &&...args) noexcept { EventManager::post(std::forward(args)...); } \ + static void post(auto &&...args) { EventManager::post(std::forward(args)...); } \ }; #define EVENT_DEF(event_name, ...) EVENT_DEF_IMPL(event_name, #event_name, true, __VA_ARGS__) @@ -72,11 +72,12 @@ namespace hex { explicit Event(Callback func) noexcept : m_func(std::move(func)) { } - void operator()(Params... params) const noexcept { + void operator()(std::string_view eventName, Params... params) const { try { m_func(params...); } catch (const std::exception &e) { - log::error("An exception occurred while handling event: {}", e.what()); + log::error("An exception occurred while handling event {}: {}", eventName, e.what()); + throw; } } @@ -173,12 +174,12 @@ namespace hex { * @param args Arguments to pass to the event */ template - static void post(auto &&...args) noexcept { + static void post(auto && ...args) { std::scoped_lock lock(getEventMutex()); for (const auto &[id, event] : getEvents()) { if (id == E::Id) { - (*static_cast(event.get()))(std::forward(args)...); + (*static_cast(event.get()))(wolv::type::getTypeName(), std::forward(args)...); } } @@ -310,4 +311,8 @@ namespace hex { */ EVENT_DEF(MovePerProviderData, prv::Provider *, prv::Provider *); + /** + * Called when ImHex managed to catch an error in a general try/catch to prevent/recover from a crash + */ + EVENT_DEF(EventCrashRecovered, const std::exception &); } \ No newline at end of file diff --git a/main/gui/source/window/window.cpp b/main/gui/source/window/window.cpp index 90ec843df..f8402b341 100644 --- a/main/gui/source/window/window.cpp +++ b/main/gui/source/window/window.cpp @@ -126,6 +126,7 @@ namespace hex { throw; } catch (const std::exception &e) { log::fatal("Unhandled exception: {}", e.what()); + EventCrashRecovered::post(e); } catch (...) { log::fatal("Unhandled exception: Unknown exception"); } @@ -133,15 +134,19 @@ namespace hex { void errorRecoverLogCallback(void*, const char* fmt, ...) { va_list args; - va_start(args, fmt); std::string message; - message.resize(std::vsnprintf(nullptr, 0, fmt, args)); - std::vsnprintf(message.data(), message.size(), fmt, args); - message.resize(message.size() - 1); + va_start(args, fmt); + message.resize(std::vsnprintf(nullptr, 0, fmt, args)); va_end(args); + va_start(args, fmt); + std::vsnprintf(message.data(), message.size(), fmt, args); + va_end(args); + + message.resize(message.size() - 1); + log::error("{}", message); } diff --git a/plugins/builtin/include/content/popups/popup_crash_recovered.hpp b/plugins/builtin/include/content/popups/popup_crash_recovered.hpp new file mode 100644 index 000000000..f4925b662 --- /dev/null +++ b/plugins/builtin/include/content/popups/popup_crash_recovered.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include + +#include + +#include + +#include + +namespace hex::plugin::builtin { + + class PopupCrashRecovered : public Popup { + public: + PopupCrashRecovered(const std::exception &e) + : hex::Popup("hex.builtin.popup.crash_recover.title", false), + m_errorType(typeid(e).name()), + m_errorMessage(e.what) { } + + void drawContent() override { + ImGuiExt::TextFormattedWrapped("hex.builtin.popup.crash_recover.message"_lang); + + ImGuiExt::TextFormattedWrapped(hex::format("Error: {}: {}", llvm::itaniumDemangle(this->m_errorType), this->m_errorMessage)); + + if (ImGui::Button("hex.ui.common.okay"_lang)) { + this->close(); + } + } + + [[nodiscard]] ImGuiWindowFlags getFlags() const override { + return ImGuiWindowFlags_AlwaysAutoResize; + } + + [[nodiscard]] ImVec2 getMinSize() const override { + return scaled({ 400, 100 }); + } + + [[nodiscard]] ImVec2 getMaxSize() const override { + return scaled({ 600, 300 }); + } + + private: + std::string m_errorType, m_errorMessage; + }; + +} \ No newline at end of file diff --git a/plugins/builtin/romfs/lang/en_US.json b/plugins/builtin/romfs/lang/en_US.json index 08f15b06b..dd2900ce4 100644 --- a/plugins/builtin/romfs/lang/en_US.json +++ b/plugins/builtin/romfs/lang/en_US.json @@ -374,6 +374,8 @@ "hex.builtin.popup.exit_application.desc": "You have unsaved changes made to your Project.\nAre you sure you want to exit?", "hex.builtin.popup.exit_application.title": "Exit Application?", "hex.builtin.popup.waiting_for_tasks.title": "Waiting for Tasks", + "hex.builtin.popup.crash_recover.title": "Crash recovery", + "hex.builtin.popup.crash_recover.message": "An exception was thrown, but ImHex was able to catch it and advert a crash", "hex.builtin.popup.blocking_task.title": "Running Task", "hex.builtin.popup.blocking_task.desc": "A task is currently executing.", "hex.builtin.popup.save_layout.title": "Save Layout", @@ -498,7 +500,7 @@ "hex.builtin.setting.toolbar.icons": "Toolbar Icons", "hex.builtin.shortcut.next_provider": "Select next provider", "hex.builtin.shortcut.prev_provider": "Select previous provider", - "hex.builtin.title_bar_button.debug_build": "Debug build", + "hex.builtin.title_bar_button.debug_build": "Debug build\nShift+Click to crash using exception\nCtrl+Click to crash using signal", "hex.builtin.title_bar_button.feedback": "Leave Feedback", "hex.builtin.tools.ascii_table": "ASCII table", "hex.builtin.tools.ascii_table.octal": "Show octal", diff --git a/plugins/builtin/source/content/events.cpp b/plugins/builtin/source/content/events.cpp index e71117455..12c4e8e51 100644 --- a/plugins/builtin/source/content/events.cpp +++ b/plugins/builtin/source/content/events.cpp @@ -21,6 +21,7 @@ #include #include #include +#include namespace hex::plugin::builtin { @@ -48,6 +49,10 @@ namespace hex::plugin::builtin { void registerEventHandlers() { static bool imhexClosing = false; + EventCrashRecovered::subscribe([](const std::exception &e) { + PopupCrashRecovered::open(e); + }); + EventWindowClosing::subscribe([](GLFWwindow *window) { imhexClosing = false; if (ImHexApi::Provider::isDirty() && !imhexClosing) {