feat(hardening): 内嵌产品配置 + 系统级状态 + 全量 i18n + 依赖治理 + 资料清理

update-client-hardening 四工作流合入(构建/静态检查已过):

- 配置:不可变产品策略经 UpdateClientResources.cmake 归一化校验后
  内嵌 qrc(URL/相对路径/主程序必须落 install_root/版本/UUID 格式,
  CMake override 写入前复验);可变机器状态迁系统级原生存储;
  运行目录不再落可编辑 app_config.json。
- i18n:生产源全英文(no-Han 检查 41/41),中文进 translations/
  zh_CN 目录;翻译加载收敛 Common/TranslationHelper 唯一入口;
  Bootstrap 补资源文件。
- 依赖:Qt/OpenSSL/架构/Perl 机器路径改 imported targets +
  UpdateClientDependencies.cmake,3rdparty 由父工程契约供给。
- 清理:旧 README/SDK README/3 个重复 PowerShell 打包脚本/PDF 提取
  文本删除,canonical README + 5 份结构化英文文档替代;Launcher
  requireAdministrator manifest;统一 MSVC /utf-8 删运行时编码设置。
This commit is contained in:
Comely
2026-07-10 00:38:23 -07:00
parent b06e003502
commit fe5aa5bbf0
45 changed files with 5033 additions and 4083 deletions
+24 -6
View File
@@ -1,5 +1,6 @@
#include <windows.h>
#include <shellapi.h>
#include "resource.h"
#include <filesystem>
#include <chrono>
#include <cstdlib>
@@ -10,6 +11,21 @@
namespace fs = std::filesystem;
static std::wstring loadLocalizedString(unsigned int id)
{
const wchar_t* value = nullptr;
const int length = LoadStringW(GetModuleHandleW(nullptr), id,
reinterpret_cast<LPWSTR>(&value), 0);
return length > 0 ? std::wstring(value, static_cast<size_t>(length)) : std::wstring();
}
static void showBootstrapError(unsigned int messageId)
{
const std::wstring message = loadLocalizedString(messageId);
const std::wstring title = loadLocalizedString(IDS_BOOTSTRAP_ERROR_TITLE);
MessageBoxW(nullptr, message.c_str(), title.c_str(), MB_ICONERROR);
}
static std::wstring quote(const std::wstring& value)
{
std::wstring result = L"\"";
@@ -115,7 +131,7 @@ int WINAPI wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
int argc = 0;
LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc);
if (!argv || argc < 11) {
MessageBoxW(nullptr, L"Bootstrap 参数不完整。", L"更新接管失败", MB_ICONERROR);
showBootstrapError(IDS_BOOTSTRAP_ARGUMENTS_INCOMPLETE);
if (argv) LocalFree(argv);
return 2;
}
@@ -142,12 +158,12 @@ int WINAPI wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
while (std::getline(input, line)) {
if (!line.empty() && line.back() == '\r') line.pop_back();
if (line.size() < 3 || line[1] != '\t' || (line[0] != 'C' && line[0] != 'D')) {
MessageBoxW(nullptr, L"更新计划操作格式无效。", L"更新接管失败", MB_ICONERROR);
showBootstrapError(IDS_BOOTSTRAP_INVALID_PLAN_OPERATION);
return 3;
}
const fs::path relative(fromUtf8(line.substr(2)));
if (!safeRelative(relative)) {
MessageBoxW(nullptr, L"更新计划包含不安全路径。", L"更新接管失败", MB_ICONERROR);
showBootstrapError(IDS_BOOTSTRAP_UNSAFE_PLAN_PATH);
return 3;
}
items.push_back({static_cast<wchar_t>(line[0]), relative});
@@ -182,9 +198,11 @@ int WINAPI wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
const std::wstring result = success ? L"success" : (rolledBack ? L"rolledback" : L"rollback-failed");
if (!launchUpdater(updater, updateArgs, result)) {
std::wstring message = L"无法重新启动 Updater.exe。";
if (!failedPath.empty()) message += L"\n失败文件:" + failedPath.wstring();
MessageBoxW(nullptr, message.c_str(), L"更新接管失败", MB_ICONERROR);
std::wstring message = loadLocalizedString(IDS_BOOTSTRAP_RESTART_UPDATER_FAILED);
if (!failedPath.empty())
message += L"\n" + loadLocalizedString(IDS_BOOTSTRAP_FAILED_FILE_PREFIX) + failedPath.wstring();
const std::wstring title = loadLocalizedString(IDS_BOOTSTRAP_ERROR_TITLE);
MessageBoxW(nullptr, message.c_str(), title.c_str(), MB_ICONERROR);
return 4;
}
return (success || rolledBack) ? 0 : 5;