/
Dengit-man
/
CustomLocaleKeyShortcut
Обзор
Документация
Войти
/
Dengit-man
/
CustomLocaleKeyShortcut
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
7
CI/CD
Аналитика
Безопасность
master
main0.cpp
771 строка
29 KB
Dengit-man
Фикс мелочей - 1.5.1
27 май 2026, 14:23
Верифицирован
27 май 2026, 14:23
28ae8d3
Код
Авторство
О чём код?
// // Created by Dengit-man on ср,21.01.2026. // // Правообладатель (c) 2026 Пользователь GitVerse "Dengit-man" // // C++23 - глобальное переключение раскладки по Shift+CapsLock // #pragma execution_character_set("utf-8") #ifdef NDEBUG #define RELEASE_TYPE "Release" #else #define RELEASE_TYPE "Debug" #endif #ifdef _BLOCK_STDIN_ #define STDIN_STATUS false #include "Classes/S_BlockStdIn.h" S_BlockStdIn BlockedStandardInput; #else #define STDIN_STATUS true #endif #include <atomic> #include <condition_variable> #include <csignal> #include <fcntl.h> #include <format> #include <iostream> #include <thread> #include <windows.h> #ifdef __MINGW32__ #include <pthread.h> // Для pthread_getw32threadhandle_np #endif // #define TERMCOLOR_TARGET_POSIX #ifdef __unix__ #undef TERMCOLOR_TARGET_WINDOWS #endif #include <ranges> #include "Classes/C_Exceptions.h" #include "Classes/C_Logger.h" #include "Classes/C_MainContext.h" #include "Classes/C_Th_DoOnSignal.h" #include "Classes/S_Utilities.h" #include "Classes/LICENSE_UTILS.h" #ifdef _MSC_VER #define WIDE_STR_COPY(one, two, three) wcsncpy_s((one), (two), (three)) #else #define WIDE_STR_COPY(one, two, three) std::wcsncpy((one), (two), (three)) #endif // ------------------------------------------------------------------ // Глобальные переменные // ------------------------------------------------------------------ HHOOK g_hook = nullptr; std::condition_variable hook_thread_started; std::atomic g_running{ true }; std::atomic g_sigterm_requested{ false }; std::atomic g_hook_installed{ false }; std::atomic<DWORD> g_hookThreadId{ 0 }; std::atomic g_toggleCount{0}; std::atomic g_pendingSwitch{false}; // ------------------------------------------------------------------ // Идентификатор пользовательского сообщения (не обязателен, но удобно) // ------------------------------------------------------------------ constexpr UINT WM_TOGGLE_GLOBAL = WM_USER + 1; // ------------------------------------------------------------------ // Список раскладок, между которыми будем переключаться // ------------------------------------------------------------------ HKL g_layoutEN = nullptr; // 00000409 - английская (US) HKL g_layoutRU = nullptr; // 00000419 - русская /// Инициализация списка раскладок используемых этим приложением void InitLayouts() { LogDebug(L"InitLayouts()", false); auto sys_layouts = C_MainContext::instance()->GetAvailableLayouts(); if (sys_layouts.empty()) { g_layoutEN = LoadKeyboardLayoutW(L"00000409", KLF_NOTELLSHELL); g_layoutRU = LoadKeyboardLayoutW(L"00000419", KLF_NOTELLSHELL); // Note: Not unloading layouts to avoid issues with active usage; acceptable for long-running tools. if (!g_layoutEN) { // std::cerr << "[-] Error while loading layout EN" << std::endl; LogCritical(L"[-] Error while loading layout EN", false); } else { LogDebug(L"[+] Loaded layout EN", true); } if (!g_layoutRU) { // std::cerr << "[-] Error while loading layout RU" << std::endl; LogCritical(L"[-] Error while loading layout RU", false); } else { LogDebug(L"[+] Loaded layout RU", true); } sys_layouts[L"00000409"] = {g_layoutEN, "EN"}; sys_layouts[L"00000419"] = {g_layoutRU, "RU"}; } //TODO 1: Потенциальная утечка ресурсов. // Возможно нужно предусмотреть такое освобождение, // но есть нюансы, если раскладка где-то активна // if (g_layoutEN) UnloadKeyboardLayout(g_layoutEN); // if (g_layoutRU) UnloadKeyboardLayout(g_layoutRU); } // ------------------------------------------------------------------ // Получить раскладку, которая сейчас активна в foreground-окне // ------------------------------------------------------------------ HKL GetForegroundLayout() { HWND fg = GetForegroundWindow(); if (!fg) { return nullptr; } DWORD threadId = GetWindowThreadProcessId(fg, nullptr); // получаем ID потока окна return GetKeyboardLayout(threadId); } static void PrintMsg(const std::string& msg, const std::string& msg1 = "") { if (msg.empty()) { return; } std::unique_lock mtx_locker(C_Logger::getPrintMutex()) ; if (msg1.empty() || msg1 == std::string("")) { std::cout << msg << " " << std::flush; } else { std::cout << msg << " -- " << msg1 << " " << std::flush; } std::cout << std::endl; } static void PrintError(const std::string& msg, const std::string& msg1 = "") { if (msg.empty()) { return; } std::unique_lock mtx_locker(C_Logger::getPrintMutex()) ; if (msg1.empty() || msg1 == std::string("")) { std::cerr << msg << " " << std::flush; } else { std::cerr << msg << ": " << msg1 << " " << std::flush; } } std::pair<HKL, std::string> GetNextKeyboardLayout() { // Определяем, какая раскладка сейчас активна в текущем потоке HKL cur = GetForegroundLayout(); auto layouts = C_MainContext::instance()->GetAvailableLayouts(); HKL next = nullptr; std::string lang; if (layouts.empty()) { next = (cur == g_layoutRU) ? g_layoutEN : g_layoutRU; lang = (next == g_layoutRU) ? "RU" : "EN"; } else { auto iterator = std::ranges::find(layouts | std::views::values, cur, [](auto && p) { return p.first; }); if (iterator == (layouts | std::views::values).end()) { iterator = (layouts | std::views::values).begin(); } else { ++iterator; if (iterator == (layouts | std::views::values).end()) { iterator = (layouts | std::views::values).begin(); } } next = (*iterator).first; lang = (*iterator).second; } return {next, lang}; } // ------------------------------------------------------------------ // Отправка широковещательного запроса о смене раскладки // ------------------------------------------------------------------ void BroadcastLayoutSwitch() { auto [next, lang] = GetNextKeyboardLayout(); // Параметры сообщения: // wParam = 0 - запрос от текущего потока // lParam = (LPARAM)next - дескриптор раскладки, которую нужно установить // HWND_BROADCAST посылает сообщение всем топ-уровневым окнам. SendMessageTimeoutW( HWND_BROADCAST, WM_INPUTLANGCHANGEREQUEST, 0, reinterpret_cast<LPARAM>(next), // SMTO_ABORTIFHUNG, SMTO_NORMAL, 300, nullptr); // std::thread(PrintMsg, InfoLine("\t\t\t\t[+] Broadcast layout switch to "), ((next == g_layoutRU) ? "RU" : "EN") ).detach(); LogInfo(std::format("[+] Broadcast layout switch to -- {}", lang), true); } // ------------------------------------------------------------------ // Отправка запроса о смене раскладки переднему окну // ------------------------------------------------------------------ void ForegroundLayoutSwitch() { auto [next, lang] = GetNextKeyboardLayout(); // Параметры сообщения: // wParam = 0 - запрос от текущего потока // lParam = (LPARAM)next - дескриптор раскладки, которую нужно установить // HWND_BROADCAST посылает сообщение всем топ-уровневым окнам. SendMessageTimeoutW( GetForegroundWindow(), WM_INPUTLANGCHANGEREQUEST, 0, reinterpret_cast<LPARAM>(next), // SMTO_ABORTIFHUNG, SMTO_NORMAL, 300, nullptr); // std::thread(PrintMsg, InfoLine("\t\t\t\t[+] Foreground layout switch to "), ((next == g_layoutRU) ? "RU" : "EN") ).detach(); LogInfo(std::format("[+] Foreground layout switch to -- {}", lang), false); } void PendToggleSwitch() { LogDebug("PendToggleSwitch()", false); if (g_toggleCount.load(std::memory_order_relaxed) > 5) { // Логируем аномалию // std::cerr << "\t[!] Too many toggle requests, resetting\n"; // std::thread(PrintError, InfoLine("\t[!] Too many toggle requests, resetting"), "").detach(); LogWarn("[!] Too many toggle requests, resetting", true); g_toggleCount.store(1, std::memory_order_relaxed); // считаем как 1 } // Увеличиваем счётчик нажатий g_toggleCount.fetch_add(1, std::memory_order_relaxed); // Если ещё не запланировано переключение — планируем, иначе игнорируем if (!g_pendingSwitch.exchange(true, std::memory_order_acquire)) { // g_pendingSwitch.notify_all(); C_MainContext::instance()->g_doOnSignal->triggerIt(); // Отправляем задачу в поток хука PostThreadMessageW(g_hookThreadId.load(), WM_TOGGLE_GLOBAL, 0, 0); } } // ------------------------------------------------------------------ // Low-level клавиатурный hook // ------------------------------------------------------------------ LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) { static auto ctx = C_MainContext::instance(); //статическая переменная для оптимизации скорости функции. if (nCode == HC_ACTION && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)) { const KBDLLHOOKSTRUCT* ks = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam); // std::wcout << ks->vkCode << "\t|\t(char_from_vkCode) " << static_cast<char>(ks->vkCode) << "\t|\t" << ks->scanCode << "\t|\t" << ks->time << std::endl; const bool caps = (ks->vkCode == ctx->GetLayoutSwitchKey().vk); const bool shift = (GetAsyncKeyState(ctx->GetLayoutSwitchKey().modifiers) & KF_UP) != 0; if (caps && shift) { // std::thread(PrintMsg, std::string("Run switch in separate thread to prevent blocking the hook\n"), "" ).detach(); PendToggleSwitch(); return 1; // подавляем дальнейшую обработку CapsLock } } return CallNextHookEx(g_hook, nCode, wParam, lParam); } // ------------------------------------------------------------------ // Поток, в котором работает message-loop для hook'а // ------------------------------------------------------------------ void HookThread() { g_hookThreadId.store(GetCurrentThreadId(), std::memory_order_relaxed); g_hook = SetWindowsHookExW(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandleW(nullptr), 0); if (!g_hook) { // std::cerr << "\t\t\t\t[-] SetWindowsHookEx failed, err: " << GetLastError() << std::endl; // std::thread(PrintError, CriticalLine("\t\t\t\t[-] SetWindowsHookEx failed, err: "), std::to_string(GetLastError())).detach(); LogCritical("[-] SetWindowsHookEx failed, err: " + std::to_string(GetLastError()), false); return; } // std::thread(PrintMsg, InfoLine("\t\t\t\t[+] Hook installed"), "").detach(); LogInfo("[+] Hook installed", false); g_hook_installed.store(true, std::memory_order_release); Sleep(5); // Задержка для корректной синхронизации. Костыль против зависшей переменной синхронизации. hook_thread_started.notify_all(); // LogDebug("Hook_thread_started notified one", false); // LogDebug(std::format("g_hook_installed stored {}", true), false); // g_hook_installed.notify_all(); // LogDebug("g_hook_installed notified all", false); LogDebug("g_hook_installed notified all", false); MSG msg; while (g_running && GetMessageW(&msg, nullptr, 0, 0)) { TranslateMessage(&msg); DispatchMessageW(&msg); } UnhookWindowsHookEx(g_hook); // std::thread(PrintMsg, InfoLine("\t\t\t\t[+] Hook removed"), "").detach(); LogInfo("[+] Hook removed", false); } void KeyLayoutSwitcher() { LogDebug("Key layout switcher is started...", false); // Сбрасываем флаг ПЕРЕД переключением g_pendingSwitch.store(false, std::memory_order_release); // Читаем счётчик const int count = g_toggleCount.load(std::memory_order_relaxed); // Сбрасываем счётчик g_toggleCount.store(0, std::memory_order_relaxed); if (count % 2 == 1) { static const SendMessageMode mode = C_MainContext::instance()->GetSendMessageMode(); switch (mode) { case SendMessageMode::Broadcast: BroadcastLayoutSwitch(); break; case SendMessageMode::ForegroundOnly: ForegroundLayoutSwitch(); break; default: const auto error = std::format("[!] SendMessageMode not supported: {}", (static_cast<bool>(mode) ? "TRUE" : "FALSE")); LogCritical(error, false); throw EInvalidValue(error); break; } } } /// Обработчик запроса на завершение void RequestShutdown() { if (!g_running.exchange(false)) // уже выключаемся - ничего не делаем { return; } LogInfo("[+] Got shutdown request, stopping hook...", false); if (g_hookThreadId.load() != 0) { PostThreadMessageW(g_hookThreadId.load(), WM_QUIT, 0, 0); } } /// Обработчик POSIX-сигналов void posix_signal_handler(int sig) { // std::wcout << L"\t\t\t\t" << L"[+] Received signal " << sig if (sig == SIGINT) // interrupt { LogWarn("[+] Received SIGINT -- interrupted", false); g_sigterm_requested.store(true, std::memory_order_relaxed); RequestShutdown(); } if (sig == SIGILL) // illegal instruction - invalid function image { std::cerr << C_Logger::getLogColor<decltype(std::cerr)::char_type>(MsgType::CriticalMsg) << FatalLine("[-] Received SIGILL -- illegal instruction - invalid function image") << \ C_Logger::getLogColor<decltype(std::cerr)::char_type>(static_cast<MsgType>(-1)) << std::endl; LogFatal("[-] Received SIGILL -- illegal instruction - invalid function image", false); g_sigterm_requested.store(true, std::memory_order_relaxed); RequestShutdown(); } if (sig == SIGFPE) // floating point exception { std::cerr << C_Logger::getLogColor<decltype(std::cerr)::char_type>(MsgType::CriticalMsg) << FatalLine("[-] Received SIGFPE -- floating point exception") << \ C_Logger::getLogColor<decltype(std::cerr)::char_type>(static_cast<MsgType>(-1)) << std::endl; LogFatal("[-] Received SIGFPE -- floating point exception", false); g_sigterm_requested.store(true, std::memory_order_relaxed); RequestShutdown(); } if (sig == SIGSEGV) // segment violation { std::cerr << C_Logger::getLogColor<decltype(std::cerr)::char_type>(MsgType::CriticalMsg) << FatalLine("[-] Received SIGSEGV -- segment violation") << \ C_Logger::getLogColor<decltype(std::cerr)::char_type>(static_cast<MsgType>(-1)) << std::endl; // LogFatal("[-] Received SIGSEGV -- segment violation", false); g_sigterm_requested.store(true, std::memory_order_relaxed); RequestShutdown(); } if (sig == SIGTERM) // Software termination signal from kill { LogInfo("[+] Received SIGTERM -- Software termination signal from kill", false); g_sigterm_requested.store(true, std::memory_order_relaxed); RequestShutdown(); } if (sig == SIGBREAK) // Ctrl-Break sequence { LogInfo("[+] Received SIGBREAK -- Ctrl-Break sequence", false); g_sigterm_requested.store(true, std::memory_order_relaxed); RequestShutdown(); } if (sig == SIGABRT) // abnormal termination triggered by abort call { LogWarn("[+] Received SIGABRT -- abnormal termination triggered by abort call", false); g_sigterm_requested.store(true, std::memory_order_relaxed); RequestShutdown(); } } /// Обработчик событий от Win-консоли BOOL WINAPI ConsoleCtrlHandler(DWORD dwCtrlType) { wchar_t sigName[20] = {0}; switch (dwCtrlType) { case CTRL_C_EVENT: WIDE_STR_COPY(sigName, L"CTRL_C_EVENT", sizeof(sigName)); case CTRL_BREAK_EVENT: WIDE_STR_COPY(sigName, L"CTRL_BREAK_EVENT", sizeof(sigName)); case CTRL_CLOSE_EVENT: WIDE_STR_COPY(sigName, L"CTRL_CLOSE_EVENT", sizeof(sigName)); case CTRL_LOGOFF_EVENT: WIDE_STR_COPY(sigName, L"CTRL_LOGOFF_EVENT", sizeof(sigName)); case CTRL_SHUTDOWN_EVENT: WIDE_STR_COPY(sigName, L"CTRL_SHUTDOWN_EVENT", sizeof(sigName)); LogInfo(std::format(L"[+] Got event ({}: {}). sending request", sigName, std::to_wstring(dwCtrlType)), false); RequestShutdown(); return TRUE; // событие обработано default: return FALSE; // неизвестное событие - передать дальше } } void inline printProjectVersion() { constexpr S_Utilities::true_color rgb{.r=15, .g=125, .b=35}; switch (S_Utilities::availableColorPalette()) { case ColorPalette::_1: break; case ColorPalette::_16: std::cout << termcolor::yellow; break; case ColorPalette::_256: std::cout << termcolor::bright_magenta; break; case ColorPalette::TrueColor: std::cout << rgb; } std::cout << std::format("Version: " _PROJ_VER_ "_" RELEASE_TYPE " | {} \n", (STDIN_STATUS) ? "STDIN enabled" : "STDIN disabled") << termcolor::reset << std::flush; } // ------------------------------------------------------------------ // Точка входа // ------------------------------------------------------------------ void printHelp() { std::cout << std::format( "Help.\n" "Custom Locale Key Shortcut -- this software allows you to use a non-standard \n" " keyboard shortcut to switch the keyboard layout.\n" "\n" "Acceptable commands:" " -h,--help -- show this help\n" " -c,--config <file_path> -- INI-config file path\n" " -l,--license -- print dependency license and this software license\n" " --no-logs -- turn ALL logs off, including most of error messages\n" " --no-input -- disable user input into standard input") << std::endl; } void printLicense() { std::cout << std::format( "Information about some licenses.\n" "\n" "License from dependency \"termcolor\"\n" "{}\n" "\n" "-----------------------------------------------\n" "Main License for this software.\n" "{}", GetDepLicense(), GetMainLicense()) << std::endl; } std::map<std::string, std::variant<bool, int, std::string> > parseArgs(int argc, char* argv[]) { LogDebug(L"Analyzing command line arguments...", false); std::map<std::string, std::variant<bool, int, std::string> > Result; for (int i = 1; i < argc; ++i) { std::string_view const arg{argv[i]}; if (arg == "-c" || arg == "--config") { const char* filename = argv[++i]; Result.emplace("config", filename); } else if (arg == "-h" || arg == "--help") { Result.emplace("help", true); break; } else if (arg == "-l" || arg == "--license") { Result.emplace("license", true); break; } else if (arg == "--no-logs") { Result.emplace("no-logs", true); } else if (arg == "--no-input") { Result.emplace("no-input", true); } else { const auto error = std::format("[-] Error. Wrong input argument. arg: \"{}\"", arg); LogCritical(error, false); throw EInvalidValue(error); } } return Result; } int main(int argc, char* argv[]) { try { printProjectVersion(); LogDebug(L"Started...", false); auto map_args = parseArgs(argc, argv); { // ограничение области существования `it` auto it = map_args.find("config"); if (it != map_args.end()) { LogDebug(std::format("Setting config file: \"{}\"", std::get<std::string>(it->second)), false); __defaults__::configFileName = std::get<std::string>(it->second); } } LogDebug(L"ParsedArgs (processing it): ", false); for (const auto& [arg_name, value] : map_args) { const char* func_name = __FUNCTION__; std::string line{}; std::visit([&line, &arg_name, func_name](const auto& arg) { line = std::format("\t\"{}\": \"{}\"", arg_name, arg); }, value); LogDebug(line, false); if (arg_name == "help") { printHelp(); return 0; } if (arg_name == "license") { printLicense(); return 0; } if (arg_name == "no-logs") { LogDebug(L"Logs Disabled", false); C_MainContext::instance()->DisableLogs(); } if (arg_name == "no-input") { LogDebug(L"Input Disabled", false); C_MainContext::instance()->DisableInput(); } } LogDebug(L"Creating \"doOnSignal\" thread", false); C_MainContext::instance()->g_doOnSignal = std::make_unique<C_Th_DoOnSignal>(KeyLayoutSwitcher); LogDebug(L"Wait until console is ready. 10 msec.", false); Sleep(10); // wait until console is ready InitLayouts(); // 1 Регистрация обработчика консольных событий LogDebug(L"Registering console events handler", false); if (!SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE)) { LogCritical(std::format("[-] Error while setting ConsoleCtrlHandler, err: {}", std::to_string(GetLastError())), true); return 1; } LogDebug(L"Windows ConsoleCtrlHandler successfuly registered", false); // 2 Обработчик POSIX-сигнала SIGTERM LogDebug(L"Registering POSIX signal handler", false); std::signal(SIGINT, posix_signal_handler); std::signal(SIGILL, posix_signal_handler); std::signal(SIGFPE, posix_signal_handler); std::signal(SIGSEGV, posix_signal_handler); std::signal(SIGTERM, posix_signal_handler); std::signal(SIGBREAK, posix_signal_handler); std::signal(SIGABRT, posix_signal_handler); LogDebug(L"Registered POSIX signal handler", false); LogDebug(L"Creating HookThread", false); std::thread thread(HookThread); // Установка высокого приоритета для потока HookThread #ifdef __MINGW32__ // MinGW: получаем HANDLE через pthread_gethandle HANDLE hThread = pthread_gethandle(pthread_t(thread.native_handle())); #else HANDLE hThread = thread.native_handle(); #endif //Приоритет выше среднего. LogDebug(L"Setting thread priotity to \"Above Normal\"", false); SetThreadPriority(hThread, THREAD_PRIORITY_ABOVE_NORMAL); { LogDebug(L"Wait until background HookThread is ready", false); std::mutex mtx; std::unique_lock mtx_locker(mtx) ; short unsigned tries = -1; const short unsigned maxtries = 5; while (++tries < maxtries) { auto rslt = hook_thread_started.wait_until(mtx_locker, std::chrono::steady_clock::now() + std::chrono::seconds(5)); if (rslt == std::cv_status::timeout) { LogWarn(std::format("[-] hook_thread_started stays locked in 5 seconds. " "Trying again: {} / {}", tries, maxtries), false); } else { break; } } if (tries >= maxtries) { auto v = std::format("[-] HookThread not signaled as ready in 5 seconds after {} tries. " "Seems like deadlock.", tries); LogFatal(v, false); throw EFatalException(v); return -5; } // hook_thread_started.wait(mtx_locker); C_MainContext::instance()->g_doOnSignal->start(); std::string line{}; if (C_MainContext::instance()->isInputEnabled()) { line = std::format("Started background hook for {}.\n" "> Enter \"quit\" (or press Ctrl+C) to exit.\n", C_MainContext::instance()->GetLayoutSwitchShortcut_string()); } else { line = std::format("Started background hook for {}.\n" "> Press Ctrl+C to exit.\n", C_MainContext::instance()->GetLayoutSwitchShortcut_string()); } std::thread(PrintMsg, line, "").detach(); } const bool expected = true; // ожидаемое значение g_hook_installed.wait(!expected); // блокируется, пока flag == !expected if (C_MainContext::instance()->isInputEnabled()) { std::cout << ">: \b\b\b" << std::flush; std::wstring cmd; while (std::wcin >> cmd) { std::wcout << L"> Entered: " << cmd << std::endl; if (cmd == L"quit") { RequestShutdown(); break; } else if (cmd == L"status") { std::thread(PrintMsg, std::string( "> Current layout in current thread: "), (GetKeyboardLayout(0) == g_layoutRU ? "RU" : "EN")).detach(); std::thread(PrintMsg, std::string("> Current layout in foreground: "), (GetForegroundLayout() == g_layoutRU ? "RU" : "EN")).detach(); } else if (cmd == L"toggle_lang") { PendToggleSwitch(); } else if (cmd == L"license") { printLicense(); std::cout << "===============================================\n" "Ready for command (or Ctrl+C) " ">: " << std::flush; } else { std::thread(PrintError, std::string("[-] Unknown command\n"), "").detach(); LogWarn("[-] Unknown command", true); } cmd = L""; } } if (thread.joinable()) { thread.join(); } std::wcout << L"\nExit.\n"; return 0; } catch (const C_Exception& e) { LogCritical(std::format("[-] Uncaught exception: [{}]: \"{}\"",e.name(), e.message()), false); throw; } catch (const std::exception& e) { LogCritical(std::format("[-] Unknown exception: {}", e.what()), false); throw; } catch (...) { LogCritical("[-] Uncaught exception", false); throw; } }