feat(backup): 支持自定义备份目录和备份跳过选项

- 在 Lua 配置中新增 backup.dir 配置项,允许设置默认备份路径
- 修改 backup_registry() 函数,接受自定义备份路径参数,优先级为:传入参数 > Lua 配置 > 默认 %APPDATA% 路径
- 在保存操作前,通过对话框让用户选择“使用默认路径”、“自定义目录”或“跳过备份”
- 更新 README 文档,说明新的备份功能和多语言支持等架构细节
This commit is contained in:
2026-04-29 16:08:58 +08:00
parent 3bc2f00cb1
commit ceed90aea8
5 changed files with 138 additions and 30 deletions
+64 -16
View File
@@ -28,23 +28,71 @@ int btn_ok_cb(Ihandle *self)
return IUP_DEFAULT;
}
ErrorCode backup_result = backup_registry();
if (backup_result != ERR_OK)
{
log_error("Backup failed: error code %d", backup_result);
const char *reason = "未知错误";
if (backup_result == ERR_FAILED)
reason = "无法获取 AppData 路径";
else if (backup_result == ERR_FILE_NOT_FOUND)
reason = "无法创建备份目录或文件";
else if (backup_result == ERR_REGISTRY_FAILED)
reason = "无法读取注册表中的 PATH 值";
// 询问用户是否自定义备份目录
char custom_backup_dir[MAX_PATH] = "";
int do_backup = 1; // 是否执行备份
char msg[512];
snprintf(msg, sizeof(msg), "备份失败!原因:%s\n\n是否继续保存?\n(继续保存可能导致无法恢复)", reason);
int choice = IupAlarm("警告", msg, "继续保存", "取消", NULL);
if (choice != 1)
return IUP_DEFAULT;
int backup_choice = IupAlarm("备份设置",
"是否自定义备份目录?\n\n"
"选择「使用默认」将备份到 %APPDATA%/PathEditor/backups/\n"
"选择「自定义目录」可选择其他位置",
"使用默认", "自定义目录", "跳过备份");
if (backup_choice == 2) // 自定义目录
{
Ihandle *filedlg = IupFileDlg();
IupSetAttribute(filedlg, "DIALOGTYPE", "DIR");
IupSetAttribute(filedlg, "TITLE", "选择备份目录");
IupPopup(filedlg, IUP_CENTER, IUP_CENTER);
if (IupGetInt(filedlg, "STATUS") != -1)
{
char *value = IupGetAttribute(filedlg, "VALUE");
if (value)
strncpy(custom_backup_dir, value, MAX_PATH - 1);
}
IupDestroy(filedlg);
if (strlen(custom_backup_dir) == 0)
{
IupMessage("提示", "未选择目录,将使用默认备份路径。");
}
}
else if (backup_choice == 3) // 跳过备份
{
int skip_confirm = IupAlarm("确认", "确定跳过备份吗?\n跳过备份可能导致无法恢复!",
"确定跳过", "返回备份", NULL);
if (skip_confirm != 1)
{
// 用户反悔,重新询问
return btn_ok_cb(self);
}
do_backup = 0;
}
// 执行备份(如果用户没有跳过)
if (do_backup)
{
const char *backup_path = strlen(custom_backup_dir) > 0 ? custom_backup_dir : NULL;
ErrorCode backup_result = backup_registry(backup_path);
if (backup_result != ERR_OK)
{
log_error("Backup failed: error code %d", backup_result);
const char *reason = "未知错误";
if (backup_result == ERR_FAILED)
reason = "无法获取 AppData 路径";
else if (backup_result == ERR_FILE_NOT_FOUND)
reason = "无法创建备份目录或文件";
else if (backup_result == ERR_REGISTRY_FAILED)
reason = "无法读取注册表中的 PATH 值";
char msg[512];
snprintf(msg, sizeof(msg), "备份失败!原因:%s\n\n是否继续保存?\n(继续保存可能导致无法恢复)", reason);
int choice = IupAlarm("警告", msg, "继续保存", "取消", NULL);
if (choice != 1)
return IUP_DEFAULT;
}
}
ErrorCode sys_ok = save_system_paths(&ctx->sys_paths);
+50 -9
View File
@@ -1,6 +1,7 @@
#include "utils/os_env.h"
#include "utils/string_ext.h"
#include "utils/logger.h"
#include "core/lua_config.h"
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
@@ -47,25 +48,65 @@ int is_path_valid(const char *path)
}
// 备份注册表
// 备份到 %APPDATA%/PathEditor/backups/ 目录下
// 参数 backup_path: 自定义备份目录路径,传 NULL 使用 Lua 配置中的默认路径
// 返回值:
// ERR_OK - 备份成功(系统和用户 PATH 都已备份)
// ERR_FAILED - AppData 路径获取失败
// ERR_FILE_NOT_FOUND - 备份文件创建失败
// ERR_REGISTRY_FAILED - 系统和用户 PATH 都读取失败
ErrorCode backup_registry(void)
ErrorCode backup_registry(const char *backup_path)
{
// 获取 AppData 路径
wchar_t appdata_path[MAX_PATH];
if (SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, appdata_path) != S_OK)
wchar_t backup_dir[MAX_PATH];
if (backup_path && strlen(backup_path) > 0)
{
log_error("Failed to get AppData path");
return ERR_FAILED;
// 使用用户指定的路径
wchar_t *wpath = utf8_to_wide(backup_path);
if (wpath)
{
wcsncpy(backup_dir, wpath, MAX_PATH - 1);
backup_dir[MAX_PATH - 1] = L'\0';
free(wpath);
}
else
{
log_error("Failed to convert backup path to wide string");
return ERR_FAILED;
}
}
else
{
// 使用默认路径:Lua 配置 > %APPDATA%/PathEditor/backups/
const char *lua_dir = lua_config_get_string("backup", "dir");
if (lua_dir && strlen(lua_dir) > 0)
{
wchar_t *wpath = utf8_to_wide(lua_dir);
if (wpath)
{
wcsncpy(backup_dir, wpath, MAX_PATH - 1);
backup_dir[MAX_PATH - 1] = L'\0';
free(wpath);
}
else
{
log_error("Failed to convert Lua backup dir to wide string");
return ERR_FAILED;
}
}
else
{
// 获取 AppData 路径作为默认值
wchar_t appdata_path[MAX_PATH];
if (SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, appdata_path) != S_OK)
{
log_error("Failed to get AppData path");
return ERR_FAILED;
}
swprintf(backup_dir, MAX_PATH, L"%s\\PathEditor\\backups", appdata_path);
}
}
// 创建备份目录(递归创建中间目录)
wchar_t backup_dir[MAX_PATH];
swprintf(backup_dir, MAX_PATH, L"%s\\PathEditor\\backups", appdata_path);
if (SHCreateDirectoryExW(NULL, backup_dir, NULL) != ERROR_SUCCESS)
{
log_error("Failed to create backup directory: %ls", backup_dir);