diff --git a/CMakeLists.txt b/CMakeLists.txt index 9bd329a..95b7559 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,7 +73,9 @@ target_include_directories(sega_mount PRIVATE src res) if (WIN32) target_link_libraries(sega_mount PRIVATE ${SEGA_WIN_LIBS} imgui comdlg32) # WinMain 入口需要 Windows 子系统(无参数时不弹控制台窗口) - set_target_properties(sega_mount PROPERTIES LINK_FLAGS "/SUBSYSTEM:WINDOWS") + # manifest 请求管理员权限(UAC 提权) + set_target_properties(sega_mount PROPERTIES LINK_FLAGS + "/SUBSYSTEM:WINDOWS /MANIFESTUAC:\"level='requireAdministrator' uiAccess='false'\"") endif () set_target_properties(sega_mount PROPERTIES OUTPUT_NAME sega_mount) diff --git a/src/driver_service.cpp b/src/driver_service.cpp index a5b8261..2296c67 100644 --- a/src/driver_service.cpp +++ b/src/driver_service.cpp @@ -84,10 +84,68 @@ namespace driver { if (!StartServiceW(hSvc, 0, nullptr)) { DWORD last = GetLastError(); if (last != ERROR_SERVICE_ALREADY_RUNNING) { - if (error) *error = "StartService 失败:" + utils::last_error_str(last); - CloseServiceHandle(hSvc); - CloseServiceHandle(hScm); - return false; + // 文件不存在(旧服务路径失效):删除旧服务,重新提取驱动并创建 + if (last == ERROR_FILE_NOT_FOUND) { + LOG_INFO("服务路径失效,重新安装驱动"); + CloseServiceHandle(hSvc); + hSvc = nullptr; + + // 删除旧服务 + hSvc = OpenServiceW(hScm, SERVICE_NAME, SERVICE_ALL_ACCESS | DELETE); + if (hSvc) { + DeleteService(hSvc); + CloseServiceHandle(hSvc); + hSvc = nullptr; + } + + // 重新提取驱动到系统目录 + wchar_t sys_dir2[MAX_PATH] = {}; + UINT sys_len2 = GetSystemDirectoryW(sys_dir2, MAX_PATH); + if (sys_len2 == 0 || sys_len2 > MAX_PATH) { + if (error) *error = "GetSystemDirectory 失败:" + utils::last_error_str(); + CloseServiceHandle(hScm); + return false; + } + std::wstring dst_path2 = std::wstring(sys_dir2) + L"\\drivers\\" + DRIVER_FILENAME; + if (!utils::extract_resource_to_file(IDR_DRIVER_SYS, dst_path2, error)) { + CloseServiceHandle(hScm); + return false; + } + LOG_INFO("驱动已提取到 {}", utils::wstr_to_str(dst_path2)); + + // 重新创建服务 + hSvc = CreateServiceW( + hScm, SERVICE_NAME, SERVICE_NAME, + SERVICE_ALL_ACCESS, + SERVICE_KERNEL_DRIVER, + SERVICE_DEMAND_START, + SERVICE_ERROR_NORMAL, + dst_path2.c_str(), + nullptr, nullptr, nullptr, nullptr, nullptr + ); + if (!hSvc) { + if (error) *error = "CreateService 失败:" + utils::last_error_str(); + CloseServiceHandle(hScm); + return false; + } + LOG_INFO("服务已重新安装:{}", utils::wstr_to_str(dst_path2)); + + // 再次尝试启动 + if (!StartServiceW(hSvc, 0, nullptr)) { + DWORD last2 = GetLastError(); + if (last2 != ERROR_SERVICE_ALREADY_RUNNING) { + if (error) *error = "StartService 失败:" + utils::last_error_str(last2); + CloseServiceHandle(hSvc); + CloseServiceHandle(hScm); + return false; + } + } + } else { + if (error) *error = "StartService 失败:" + utils::last_error_str(last); + CloseServiceHandle(hSvc); + CloseServiceHandle(hScm); + return false; + } } } diff --git a/src/gui/gui_common.h b/src/gui/gui_common.h index 2c2f7b8..441639a 100644 --- a/src/gui/gui_common.h +++ b/src/gui/gui_common.h @@ -12,7 +12,6 @@ inline bool g_unmounting = false; inline std::vector g_log_lines; inline std::mutex g_log_mutex; inline bool g_auto_scroll = true; -inline bool g_log_capture_enabled = false; // ===== DX11 全局变量 ===== inline ID3D11Device *g_pd3dDevice = nullptr; diff --git a/src/gui/gui_utils.cpp b/src/gui/gui_utils.cpp index ba00bba..8c4dd49 100644 --- a/src/gui/gui_utils.cpp +++ b/src/gui/gui_utils.cpp @@ -1,7 +1,9 @@ +#define LOG_MODULE "GUI" #include "gui_utils.h" #include "gui_common.h" #include "config.h" #include "utils/strings.h" +#include "utils/log.h" #include #include #include @@ -12,9 +14,9 @@ void load_config_file(const std::wstring &path) { g_config_path = utils::wstr_to_str(path); std::string err; if (parse_config(path, g_cfg, &err)) { - g_log_lines.push_back(std::format("[INFO] 配置已加载:{}", g_config_path)); + LOG_INFO("配置已加载:{}", g_config_path); } else { - g_log_lines.push_back(std::format("[ERROR] {}", err)); + LOG_ERROR("{}", err); } } diff --git a/src/gui/ui.cpp b/src/gui/ui.cpp index 66814f8..f16d5c5 100644 --- a/src/gui/ui.cpp +++ b/src/gui/ui.cpp @@ -1,3 +1,4 @@ +#define LOG_MODULE "UI" #include "ui.h" #include "gui_common.h" #include "gui_utils.h" @@ -6,13 +7,19 @@ #include "resource.h" #include "utils/resource.h" #include "utils/strings.h" +#include "utils/log.h" #include "imgui.h" #include "icons/lucide.h" #include #include -// 初始化 ImGui:主题、DPI 缩放、字体加载 +// 初始化 ImGui:主题、DPI 缩放、字体加载、日志捕获 void InitUI(float main_scale) { + // 注册日志 sink:将 LOG_XXX 输出同步到 GUI 日志面板 + logger::set_sink([](logger::Level level, const std::string &line) { + std::lock_guard lock(g_log_mutex); + g_log_lines.push_back(line); + }); ImGuiIO &io = ImGui::GetIO(); io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; @@ -82,7 +89,7 @@ void DrawUI() { g_config_path_w = utils::to_wstring(g_config_path); std::string err; save_config(g_config_path_w, g_cfg, &err); - g_log_lines.emplace_back("[INFO] 配置已保存"); + LOG_INFO("配置已保存"); } // 主题切换按钮 @@ -233,13 +240,12 @@ void DrawUI() { if (ImGui::Button(g_driver_ready ? (ICON_PLAY " 挂载") : (ICON_CIRCLE_ALERT " 挂载(需要驱动)"))) { if (g_driver_ready && !g_mounting) { g_mounting = true; - g_log_lines.emplace_back("[INFO] 开始挂载..."); + LOG_INFO("开始挂载..."); auto result = icf_mount::mount_icf_apps(g_cfg); if (result.success) { - g_log_lines.push_back(std::format("[INFO] 挂载成功:{} 个 APP,{} 个 OPT", - result.apps.size(), result.opts.size())); + LOG_INFO("挂载成功:{} 个 APP,{} 个 OPT", result.apps.size(), result.opts.size()); } else { - g_log_lines.push_back(std::format("[ERROR] 挂载失败:{}", result.error)); + LOG_ERROR("挂载失败:{}", result.error); } g_mounting = false; } @@ -249,13 +255,12 @@ void DrawUI() { if (ImGui::Button(ICON_SQUARE " 卸载")) { if (g_driver_ready && !g_unmounting) { g_unmounting = true; - g_log_lines.emplace_back("[INFO] 开始卸载..."); + LOG_INFO("开始卸载..."); auto result = icf_mount::unmount_icf_apps(g_cfg); if (result.success) { - g_log_lines.push_back(std::format("[INFO] 卸载成功:{} 个 APP,{} 个 OPT", - result.apps.size(), result.opts.size())); + LOG_INFO("卸载成功:{} 个 APP,{} 个 OPT", result.apps.size(), result.opts.size()); } else { - g_log_lines.push_back(std::format("[ERROR] 卸载失败:{}", result.error)); + LOG_ERROR("卸载失败:{}", result.error); } g_unmounting = false; } @@ -271,11 +276,11 @@ void DrawUI() { ImGui::BeginChild("LogPanel", ImVec2(-1, -1), ImGuiChildFlags_Borders); { - std::lock_guard lock(g_log_mutex); + std::lock_guard lock(g_log_mutex); for (const auto &line: g_log_lines) { if (line.find("[ERROR]") != std::string::npos) ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.3f, 0.3f, 1.0f)); - else if (line.find("[WARN]") != std::string::npos) + else if (line.find("[WARN ") != std::string::npos || line.find("[WARN]") != std::string::npos) ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.8f, 0.2f, 1.0f)); else ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.7f, 0.7f, 0.7f, 1.0f)); diff --git a/src/icf_mount.cpp b/src/icf_mount.cpp index 7be1fce..6d3a73b 100644 --- a/src/icf_mount.cpp +++ b/src/icf_mount.cpp @@ -191,6 +191,7 @@ namespace icf_mount { if (!mount_res.success) { LOG_ERROR("挂载 OPT {} 失败:{}", entry->version, mount_res.error); + all_ok = false; } result.opts.push_back(mount_entry); @@ -222,7 +223,7 @@ namespace icf_mount { result.success = all_ok; if (!all_ok) { - result.error = "部分 APP 挂载失败"; + result.error = "部分容器挂载失败,详见日志"; } return result; } diff --git a/src/main.cpp b/src/main.cpp index 46fe836..fd6977c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -146,9 +146,9 @@ static int run_gui(HINSTANCE hInstance) { std::string svc_err; g_driver_ready = driver::ensure_running(&svc_err); if (g_driver_ready) - g_log_lines.emplace_back("[INFO] 驱动服务已就绪"); + LOG_INFO("驱动服务已就绪"); else - g_log_lines.push_back(std::format("[ERROR] 驱动:{}", svc_err)); + LOG_ERROR("驱动:{}", svc_err); } // 初始化 ImGui DPI 感知 @@ -186,9 +186,6 @@ static int run_gui(HINSTANCE hInstance) { ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext); - // 启用日志捕获 - g_log_capture_enabled = true; - // 主循环 bool done = false; while (!done) { @@ -247,7 +244,6 @@ static int run_gui(HINSTANCE hInstance) { } // 清理 - g_log_capture_enabled = false; ImGui_ImplDX11_Shutdown(); ImGui_ImplWin32_Shutdown(); ImGui::DestroyContext(); diff --git a/src/utils/log.cpp b/src/utils/log.cpp index e40f9a3..ce18ed5 100644 --- a/src/utils/log.cpp +++ b/src/utils/log.cpp @@ -6,6 +6,9 @@ namespace logger { // 全局最低输出等级 static auto g_min_level = Level::Debug; + // 日志 sink 回调 + static LogSink g_sink = nullptr; + // 等级对应的标签文字(固定5字符宽度对齐) static const char *level_tag(Level level) { switch (level) { @@ -32,11 +35,19 @@ namespace logger { g_min_level = level; } + void set_sink(LogSink sink) { + g_sink = std::move(sink); + } + void output_raw(Level level, const char *module, const std::string &message) { if (static_cast(level) < static_cast(g_min_level)) return; + // 组装完整日志行: [LEVEL][MODULE] message + std::string line = std::string("[") + level_tag(level) + "][" + module + "] " + message; + + // 控制台输出 HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE); - WORD orig_attr = 7; // 默认灰白 + WORD orig_attr = 7; if (hStdout != INVALID_HANDLE_VALUE) { CONSOLE_SCREEN_BUFFER_INFO csbi{}; if (GetConsoleScreenBufferInfo(hStdout, &csbi)) { @@ -44,19 +55,16 @@ namespace logger { } SetConsoleTextAttribute(hStdout, level_color(level)); } - - // 输出: [LEVEL][MODULE] message\n - std::fputs("[", stdout); - std::fputs(level_tag(level), stdout); - std::fputs("][", stdout); - std::fputs(module, stdout); - std::fputs("] ", stdout); - std::fputs(message.c_str(), stdout); + std::fputs(line.c_str(), stdout); std::fputc('\n', stdout); std::fflush(stdout); - if (hStdout != INVALID_HANDLE_VALUE) { SetConsoleTextAttribute(hStdout, orig_attr); } + + // sink 回调(GUI 捕获) + if (g_sink) { + g_sink(level, line); + } } } diff --git a/src/utils/log.h b/src/utils/log.h index 2a858dd..84e6a76 100644 --- a/src/utils/log.h +++ b/src/utils/log.h @@ -2,6 +2,7 @@ #include #include +#include namespace logger { // 日志等级 @@ -12,9 +13,15 @@ namespace logger { Error }; + // 日志 sink 回调类型:接收完整格式化行 "[LEVEL][MODULE] message" + using LogSink = std::function; + // 设置全局最低输出等级(低于此等级的日志不输出),默认 Debug(全部输出) void set_min_level(Level level); + // 设置日志 sink 回调(传入 nullptr 取消),每次输出时会额外调用 + void set_sink(LogSink sink); + // 原始字符串输出(已格式化完成),带模块名 void output_raw(Level level, const char *module, const std::string &message);