feat: 新增系统/用户变量分离、搜索、拖拽和清理功能

- 将单一列表拆分为系统和用户变量两个标签页
- 新增搜索框支持实时过滤路径
- 支持拖拽文件夹直接添加到列表
- 新增一键清理功能移除无效和重复路径
- 增加注册表备份机制和删除确认
- 优化界面布局和权限提示逻辑
This commit is contained in:
2026-03-16 19:58:41 +08:00
parent f21d302565
commit 39d06e20e0
15 changed files with 668 additions and 209 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+10
View File
@@ -10,11 +10,21 @@ int btn_browse_cb(Ihandle* self);
int btn_del_cb(Ihandle *self);
int btn_up_cb(Ihandle *self);
int btn_down_cb(Ihandle *self);
int btn_clean_cb(Ihandle *self);
int btn_ok_cb(Ihandle *self);
int btn_cancel_cb(Ihandle *self);
int btn_help_cb(Ihandle *self);
// 搜索回调
int txt_search_cb(Ihandle *self);
// 双击回调
int list_dblclick_cb(Ihandle *self, int item, char *text);
// 拖拽回调
int list_dropfiles_cb(Ihandle *self, const char *filename, int num, int x, int y);
// 键盘按键回调
int list_k_any_cb(Ihandle *self, int c);
#endif // CALLBACKS_H
+22 -2
View File
@@ -4,12 +4,15 @@
#include <iup.h>
// 注册表路径常量
#define REG_PATH L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment"
#define REG_PATH_SYS L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment"
#define REG_PATH_USER L"Environment"
#define REG_VALUE L"Path"
// 全局控件句柄声明
extern Ihandle *dlg; // 主对话框句柄
extern Ihandle *list_path; // 路径列表控件句柄
extern Ihandle *tabs_main; // 标签页容器
extern Ihandle *list_sys; // 系统变量列表
extern Ihandle *list_user; // 用户变量列表
extern Ihandle *lbl_status; // 状态标签句柄
extern Ihandle *btn_new; // 新增按钮句柄
extern Ihandle *btn_edit; // 编辑按钮句柄
@@ -17,8 +20,25 @@ extern Ihandle *btn_browse; // 浏览按钮句柄
extern Ihandle *btn_del; // 删除按钮句柄
extern Ihandle *btn_up; // 上移按钮句柄
extern Ihandle *btn_down; // 下移按钮句柄
extern Ihandle *btn_clean; // 一键清理按钮句柄
extern Ihandle *btn_ok; // 确认按钮句柄
extern Ihandle *btn_cancel; // 取消按钮句柄
extern Ihandle *btn_help; // 帮助按钮句柄
extern Ihandle *txt_search; // 搜索框
// 简单字符串列表结构,用于搜索缓存
typedef struct {
char **items;
int count;
int capacity;
} StringList;
extern StringList raw_sys_paths;
extern StringList raw_user_paths;
// 缓存操作函数声明
void init_string_list(StringList *list);
void add_string_list(StringList *list, const char *str);
void clear_string_list(StringList *list);
#endif // GLOBALS_H
+3 -3
View File
@@ -1,10 +1,10 @@
#ifndef REGISTRY_H
#define REGISTRY_H
// 从注册表加载PATH到列表控件
void load_path();
// 从注册表加载所有PATH到列表控件
void load_all_paths();
// 将列表控件中的PATH保存回注册表
void save_path();
void save_all_paths();
#endif // REGISTRY_H
+11
View File
@@ -3,6 +3,7 @@
#include <windows.h>
#include <wchar.h>
#include <iup.h>
// 宽字符转UTF-8
char* wide_to_utf8(const wchar_t* wstr);
@@ -13,7 +14,17 @@ wchar_t* utf8_to_wide(const char* str);
// 检查管理员权限
int check_admin();
// 检查路径是否有效(存在且为目录)
int is_path_valid(const char *path);
// 刷新列表样式(斑马纹)
void refresh_list_style();
void refresh_single_list_style(Ihandle *list);
// 备份注册表
void backup_registry();
// 不区分大小写的字符串查找
char *stristr(const char *haystack, const char *needle);
#endif // UTILS_H
+258 -38
View File
@@ -27,8 +27,8 @@ int show_custom_input_dialog(const char *title, const char *label_text, char *bu
text,
IupHbox(
IupFill(),
IupButton("确定", "1"), // "1" will be returned by IupPopup
IupButton("取消", "0"), // "0" will be returned by IupPopup
IupButton("确定", "1"), // "1" 会被返回
IupButton("取消", "0"), // "0" 会被返回
NULL),
NULL));
@@ -132,6 +132,20 @@ int custom_input_dialog(const char *title, const char *label_text, char *buffer,
return result;
}
// 辅助函数:获取当前选中的列表
Ihandle *get_current_list()
{
// 获取当前选中的 Tab 索引
// 注意:IupTabs 的 VALUE 属性在某些版本可能返回 handle,某些版本返回 pos
// 这里使用 IupGetInt(tabs_main, "VALUEPOS") 更稳妥
int pos = IupGetInt(tabs_main, "VALUEPOS");
if (pos == 0)
return list_sys;
if (pos == 1)
return list_user;
return list_sys; // 默认
}
// 按钮回调:新建
int btn_new_cb(Ihandle *self)
{
@@ -140,13 +154,14 @@ int btn_new_cb(Ihandle *self)
{
if (strlen(buffer) > 0)
{
int count = IupGetInt(list_path, "COUNT");
Ihandle *current_list = get_current_list();
int count = IupGetInt(current_list, "COUNT");
count++;
IupSetAttributeId(list_path, "", count, buffer);
IupSetInt(list_path, "COUNT", count);
IupSetInt(list_path, "VALUE", count);
IupSetAttributeId(current_list, "", count, buffer);
IupSetInt(current_list, "COUNT", count);
IupSetInt(current_list, "VALUE", count);
refresh_list_style();
refresh_single_list_style(current_list);
}
}
return IUP_DEFAULT;
@@ -155,11 +170,12 @@ int btn_new_cb(Ihandle *self)
// 按钮回调:编辑
int btn_edit_cb(Ihandle *self)
{
int selected = IupGetInt(list_path, "VALUE");
Ihandle *current_list = get_current_list();
int selected = IupGetInt(current_list, "VALUE");
if (selected == 0)
return IUP_DEFAULT;
char *current_val = IupGetAttributeId(list_path, "", selected);
char *current_val = IupGetAttributeId(current_list, "", selected);
char buffer[4096]; // 假设单个路径不超过4096
if (current_val)
{
@@ -175,8 +191,8 @@ int btn_edit_cb(Ihandle *self)
{
if (strlen(buffer) > 0)
{
IupSetAttributeId(list_path, "", selected, buffer);
refresh_list_style();
IupSetAttributeId(current_list, "", selected, buffer);
refresh_single_list_style(current_list);
}
}
return IUP_DEFAULT;
@@ -185,11 +201,11 @@ int btn_edit_cb(Ihandle *self)
// 双击回调
int list_dblclick_cb(Ihandle *self, int item, char *text)
{
// 这里的 item 是点击的行号
// 这里的 self 就是触发双击的列表控件
if (item > 0)
{
// 选中该行
IupSetInt(list_path, "VALUE", item);
IupSetInt(self, "VALUE", item);
// 调用编辑逻辑
btn_edit_cb(NULL);
}
@@ -210,42 +226,100 @@ int btn_browse_cb(Ihandle *self)
char *value = IupGetAttribute(filedlg, "VALUE");
if (value)
{
int count = IupGetInt(list_path, "COUNT");
Ihandle *current_list = get_current_list();
int count = IupGetInt(current_list, "COUNT");
count++;
IupSetAttributeId(list_path, "", count, value);
IupSetInt(list_path, "COUNT", count);
IupSetInt(list_path, "VALUE", count);
IupSetAttributeId(current_list, "", count, value);
IupSetInt(current_list, "COUNT", count);
IupSetInt(current_list, "VALUE", count);
refresh_list_style();
refresh_single_list_style(current_list);
}
}
IupDestroy(filedlg);
return IUP_DEFAULT;
}
// 辅助函数:从 raw_data 中删除指定字符串
static void remove_from_raw_data(StringList *list, const char *str)
{
if (!list || !str)
return;
for (int i = 0; i < list->count; i++)
{
if (strcmp(list->items[i], str) == 0)
{
free(list->items[i]);
// 移动后续元素
for (int j = i; j < list->count - 1; j++)
{
list->items[j] = list->items[j + 1];
}
list->count--;
break; // 假设没有重复,只删除第一个匹配
}
}
}
// 按钮回调:删除
int btn_del_cb(Ihandle *self)
{
int selected = IupGetInt(list_path, "VALUE");
Ihandle *current_list = get_current_list();
int selected = IupGetInt(current_list, "VALUE");
if (selected == 0)
{
IupMessage("提示", "请先选择要删除的项");
return IUP_DEFAULT;
}
IupSetAttribute(list_path, "REMOVEITEM", "SELECTED");
// 获取当前要删除的内容
char *val = IupGetAttributeId(current_list, "", selected);
// 确认删除
// char msg[1024];
// snprintf(msg, sizeof(msg), "确定要删除以下路径吗?\n\n%s", val ? val : "(空)");
// if (IupAlarm("确认删除", msg, "是", "否", NULL) != 1)
// return IUP_DEFAULT;
// 从 raw_data 缓存中同步删除
int pos = IupGetInt(tabs_main, "VALUEPOS");
StringList *raw_data = (pos == 0) ? &raw_sys_paths : &raw_user_paths;
// 注意:必须先保存 val 的副本,因为 REMOVEITEM 可能会导致 val 指针失效(如果它是指向列表内部缓冲区的)
char *val_copy = val ? _strdup(val) : NULL;
// 先从界面删除
// IupSetAttribute(current_list, "REMOVEITEM", "SELECTED");
// 改为按索引删除,防止失去焦点导致 SELECTED 失效
IupSetInt(current_list, "REMOVEITEM", selected);
// 再从缓存删除
if (val_copy && raw_data)
{
remove_from_raw_data(raw_data, val_copy);
free(val_copy);
}
// 重新刷新
refresh_single_list_style(current_list);
// 更新状态栏,告知用户删除了什么
IupSetAttribute(lbl_status, "TITLE", "状态: 已删除选中项");
// 重新刷新,因为删除了中间项,后面的奇偶性变了
refresh_list_style();
return IUP_DEFAULT;
}
// 按钮回调:上移
int btn_up_cb(Ihandle *self)
{
int selected = IupGetInt(list_path, "VALUE");
Ihandle *current_list = get_current_list();
int selected = IupGetInt(current_list, "VALUE");
if (selected <= 1)
return IUP_DEFAULT; // 已经是第一个或未选中
char *current = IupGetAttributeId(list_path, "", selected);
char *prev = IupGetAttributeId(list_path, "", selected - 1);
char *current = IupGetAttributeId(current_list, "", selected);
char *prev = IupGetAttributeId(current_list, "", selected - 1);
// 交换内容
char buf_curr[4096], buf_prev[4096];
@@ -254,26 +328,27 @@ int btn_up_cb(Ihandle *self)
strncpy(buf_prev, prev, 4096);
buf_prev[4095] = '\0';
IupSetAttributeId(list_path, "", selected, buf_prev);
IupSetAttributeId(list_path, "", selected - 1, buf_curr);
IupSetAttributeId(current_list, "", selected, buf_prev);
IupSetAttributeId(current_list, "", selected - 1, buf_curr);
IupSetInt(list_path, "VALUE", selected - 1);
IupSetInt(current_list, "VALUE", selected - 1);
// 刷新样式(虽然颜色不需要变,但为了保险)
refresh_list_style();
refresh_single_list_style(current_list);
return IUP_DEFAULT;
}
// 按钮回调:下移
int btn_down_cb(Ihandle *self)
{
int selected = IupGetInt(list_path, "VALUE");
int count = IupGetInt(list_path, "COUNT");
Ihandle *current_list = get_current_list();
int selected = IupGetInt(current_list, "VALUE");
int count = IupGetInt(current_list, "COUNT");
if (selected == 0 || selected >= count)
return IUP_DEFAULT;
char *current = IupGetAttributeId(list_path, "", selected);
char *next = IupGetAttributeId(list_path, "", selected + 1);
char *current = IupGetAttributeId(current_list, "", selected);
char *next = IupGetAttributeId(current_list, "", selected + 1);
char buf_curr[4096], buf_next[4096];
strncpy(buf_curr, current, 4096);
@@ -281,19 +356,164 @@ int btn_down_cb(Ihandle *self)
strncpy(buf_next, next, 4096);
buf_next[4095] = '\0';
IupSetAttributeId(list_path, "", selected, buf_next);
IupSetAttributeId(list_path, "", selected + 1, buf_curr);
IupSetAttributeId(current_list, "", selected, buf_next);
IupSetAttributeId(current_list, "", selected + 1, buf_curr);
IupSetInt(list_path, "VALUE", selected + 1);
IupSetInt(current_list, "VALUE", selected + 1);
refresh_list_style();
refresh_single_list_style(current_list);
return IUP_DEFAULT;
}
// 按钮回调:一键清理
int btn_clean_cb(Ihandle *self)
{
Ihandle *current_list = get_current_list();
int count = IupGetInt(current_list, "COUNT");
if (count == 0)
return IUP_DEFAULT;
// 弹出确认对话框
if (IupAlarm("确认清理", "此操作将移除当前列表中所有【无效路径】和【重复路径】。\n确定要继续吗?", "确定", "取消", NULL) != 1)
{
return IUP_DEFAULT;
}
// 从后往前遍历删除,避免索引错位
for (int i = count; i >= 1; i--)
{
char *item = IupGetAttributeId(current_list, "", i);
if (!item)
continue;
int should_remove = 0;
// 1. 检查有效性
if (!is_path_valid(item))
{
should_remove = 1;
}
else
{
// 2. 检查重复 (检查当前项之前是否出现过)
// 注意:这里需要再次遍历,性能稍低但最稳妥
for (int j = 1; j < i; j++)
{
char *prev_item = IupGetAttributeId(current_list, "", j);
if (prev_item && _stricmp(item, prev_item) == 0)
{
should_remove = 1;
break;
}
}
}
if (should_remove)
{
IupSetAttributeId(current_list, "REMOVEITEM", i, NULL);
}
}
refresh_single_list_style(current_list);
IupMessage("提示", "清理完成!");
return IUP_DEFAULT;
}
// 搜索回调
int txt_search_cb(Ihandle *self)
{
char *filter = IupGetAttribute(self, "VALUE");
if (!filter)
return IUP_DEFAULT;
// 获取当前选中的 Tab 索引
int pos = IupGetInt(tabs_main, "VALUEPOS");
Ihandle *current_list = (pos == 0) ? list_sys : list_user;
StringList *raw_data = (pos == 0) ? &raw_sys_paths : &raw_user_paths;
// 清空列表
IupSetAttribute(current_list, "REMOVEITEM", "ALL");
// 重新填充
int count = 0;
for (int i = 0; i < raw_data->count; i++)
{
// 如果 filter 为空,或包含 filter (不区分大小写)
if (strlen(filter) == 0 || stristr(raw_data->items[i], filter) != NULL)
{
count++;
IupSetAttributeId(current_list, "", count, raw_data->items[i]);
}
}
IupSetInt(current_list, "COUNT", count);
refresh_single_list_style(current_list);
return IUP_DEFAULT;
}
// 拖拽回调
int list_dropfiles_cb(Ihandle *self, const char *filename, int num, int x, int y)
{
// 获取当前列表和原始数据
// 注意:拖拽的目标列表可能是 list_sys 或 list_user,由 self 参数决定
// 但为了确保数据一致性,我们还是重新获取一下
Ihandle *current_list = self;
StringList *raw_data = NULL;
if (self == list_sys)
raw_data = &raw_sys_paths;
else if (self == list_user)
raw_data = &raw_user_paths;
else
return IUP_DEFAULT; // 异常情况
// 检查拖入的是否为目录
DWORD attr = GetFileAttributesA(filename);
if (attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY))
{
// 如果正在搜索,先清空搜索框
IupSetAttribute(txt_search, "VALUE", "");
// 添加到列表末尾
int count = IupGetInt(current_list, "COUNT");
count++;
IupSetAttributeId(current_list, "", count, filename);
IupSetInt(current_list, "COUNT", count);
IupSetInt(current_list, "VALUE", count); // 选中新添加的项
// 同时添加到原始数据缓存,确保搜索时能搜到
if (raw_data)
{
add_string_list(raw_data, filename);
}
refresh_single_list_style(current_list);
}
else
{
// 如果拖入的不是文件夹,可以在状态栏提示
IupSetAttribute(lbl_status, "TITLE", "提示: 只能拖拽文件夹添加到 PATH");
}
return IUP_DEFAULT;
}
// 键盘按键回调
int list_k_any_cb(Ihandle *self, int c)
{
// 处理 Delete 键
if (c == K_DEL)
{
btn_del_cb(NULL);
return IUP_IGNORE; // 阻止默认处理
}
return IUP_DEFAULT;
}
// 按钮回调:确定
int btn_ok_cb(Ihandle *self)
{
save_path();
save_all_paths();
return IUP_DEFAULT;
}
+64 -20
View File
@@ -8,10 +8,32 @@
#include "registry.h"
#include "callbacks.h"
// 全局变量定义
StringList raw_sys_paths = {0};
StringList raw_user_paths = {0};
// 全局控件定义
Ihandle *dlg, *list_path, *lbl_status;
Ihandle *btn_new, *btn_edit, *btn_browse, *btn_del, *btn_up, *btn_down;
Ihandle *btn_ok, *btn_cancel, *btn_help;
Ihandle *dlg, *tabs_main, *list_sys, *list_user, *lbl_status; // 主窗口、标签页、系统路径列表、用户路径列表、状态标签
Ihandle *btn_new, *btn_edit, *btn_browse, *btn_del, *btn_up, *btn_down; // 右侧按钮
Ihandle *btn_ok, *btn_cancel, *btn_help; // 确认取消帮助按钮
Ihandle *btn_clean; // 一键清理按钮
Ihandle *txt_search; // 搜索框
// 辅助函数:创建列表控件
Ihandle *create_path_list()
{
Ihandle *list = IupFlatList();
IupSetAttribute(list, "EXPAND", "YES");
IupSetAttribute(list, "ITEMPADDING", "5x5");
IupSetAttribute(list, "BACKCOLOR", "255 255 255");
IupSetAttribute(list, "BORDER", "YES");
IupSetAttribute(list, "CANFOCUS", "YES");
IupSetAttribute(list, "HLINE", "NO");
IupSetCallback(list, "DBLCLICK_CB", (Icallback)list_dblclick_cb);
IupSetCallback(list, "DROPFILES_CB", (Icallback)list_dropfiles_cb);
IupSetCallback(list, "K_ANY", (Icallback)list_k_any_cb);
return list;
}
// 主函数
int main(int argc, char **argv)
@@ -22,15 +44,24 @@ int main(int argc, char **argv)
IupOpen(&argc, &argv);
IupSetGlobal("UTF8MODE", "YES");
// 创建列表控件
list_path = IupFlatList();
IupSetAttribute(list_path, "EXPAND", "YES");
IupSetAttribute(list_path, "ITEMPADDING", "5x5");
IupSetAttribute(list_path, "BACKCOLOR", "255 255 255");
IupSetAttribute(list_path, "BORDER", "YES");
IupSetAttribute(list_path, "CANFOCUS", "YES");
IupSetAttribute(list_path, "HLINE", "NO"); // 禁用横线,使用斑马纹
// IupFlatList 不支持 VISIBLELINES,高度由 EXPAND 和布局决定
// 创建两个列表控件
list_sys = create_path_list();
list_user = create_path_list();
// 创建搜索框
txt_search = IupText(NULL);
IupSetAttribute(txt_search, "EXPAND", "HORIZONTAL");
IupSetAttribute(txt_search, "CUEBANNER", "输入关键词搜索...");
IupSetCallback(txt_search, "VALUECHANGED_CB", (Icallback)txt_search_cb);
// 创建 Tabs
tabs_main = IupTabs(
IupVbox(list_sys, NULL),
IupVbox(list_user, NULL),
NULL);
IupSetAttribute(tabs_main, "TABTITLE0", "系统变量 (System)");
IupSetAttribute(tabs_main, "TABTITLE1", "用户变量 (User)");
IupSetAttribute(tabs_main, "TABTYPE", "TOP");
// 创建右侧按钮
btn_new = IupButton("新建(N)", NULL);
@@ -39,6 +70,7 @@ int main(int argc, char **argv)
btn_del = IupButton("删除(D)", NULL);
btn_up = IupButton("上移(U)", NULL);
btn_down = IupButton("下移(O)", NULL);
btn_clean = IupButton("一键清理", NULL);
// 设置按钮回调
IupSetCallback(btn_new, "ACTION", (Icallback)btn_new_cb);
@@ -47,9 +79,7 @@ int main(int argc, char **argv)
IupSetCallback(btn_del, "ACTION", (Icallback)btn_del_cb);
IupSetCallback(btn_up, "ACTION", (Icallback)btn_up_cb);
IupSetCallback(btn_down, "ACTION", (Icallback)btn_down_cb);
// 设置双击回调
IupSetCallback(list_path, "DBLCLICK_CB", (Icallback)list_dblclick_cb);
IupSetCallback(btn_clean, "ACTION", (Icallback)btn_clean_cb);
// 设置按钮大小 (宽度和高度都增加约1/4)
IupSetAttribute(btn_new, "RASTERSIZE", "100x32");
@@ -58,17 +88,20 @@ int main(int argc, char **argv)
IupSetAttribute(btn_del, "RASTERSIZE", "100x32");
IupSetAttribute(btn_up, "RASTERSIZE", "100x32");
IupSetAttribute(btn_down, "RASTERSIZE", "100x32");
IupSetAttribute(btn_clean, "RASTERSIZE", "100x32");
Ihandle *vbox_btns = IupVbox(
btn_new, btn_edit, btn_browse, btn_del,
IupFill(), // 间隔
btn_clean, // 放在上移下移之前,或者最下面,这里放在中间偏下
IupFill(),
btn_up, btn_down,
NULL);
IupSetAttribute(vbox_btns, "GAP", "5");
IupSetAttribute(vbox_btns, "MARGIN", "0x0");
// 上部布局:列表 + 按钮
Ihandle *hbox_main = IupHbox(list_path, vbox_btns, NULL);
// 上部布局:Tabs + 按钮
Ihandle *hbox_main = IupHbox(tabs_main, vbox_btns, NULL);
IupSetAttribute(hbox_main, "GAP", "10");
IupSetAttribute(hbox_main, "MARGIN", "10x10");
@@ -96,7 +129,8 @@ int main(int argc, char **argv)
// 总体布局
Ihandle *vbox_all = IupVbox(
IupLabel("系统变量 Path:"),
IupLabel("环境变量编辑器:"),
txt_search,
hbox_main,
hbox_bottom,
NULL);
@@ -106,7 +140,7 @@ int main(int argc, char **argv)
// 创建对话框
dlg = IupDialog(vbox_all);
IupSetAttribute(dlg, "TITLE", "编辑环境变量 (IUP版)");
IupSetAttribute(dlg, "SIZE", "450x350");
IupSetAttribute(dlg, "SIZE", "500x400"); // 稍微调大一点
IupSetAttribute(dlg, "MINBOX", "NO");
IupSetAttribute(dlg, "MAXBOX", "NO");
@@ -116,13 +150,23 @@ int main(int argc, char **argv)
IupMessage("警告", "程序未以管理员身份运行,您只能查看,无法保存更改!");
IupSetAttribute(dlg, "TITLE", "编辑环境变量 (只读模式)");
IupSetAttribute(lbl_status, "TITLE", "状态: 只读模式 (权限不足)");
// 禁用修改类按钮
IupSetAttribute(btn_new, "ACTIVE", "NO");
IupSetAttribute(btn_edit, "ACTIVE", "NO");
IupSetAttribute(btn_browse, "ACTIVE", "NO");
IupSetAttribute(btn_del, "ACTIVE", "NO");
IupSetAttribute(btn_up, "ACTIVE", "NO");
IupSetAttribute(btn_down, "ACTIVE", "NO");
IupSetAttribute(btn_clean, "ACTIVE", "NO");
IupSetAttribute(btn_ok, "ACTIVE", "NO");
}
IupShowXY(dlg, IUP_CENTER, IUP_CENTER);
// IUP List APPEND 属性需要在控件 Map 之后才能生效
// IupShowXY 会触发 Map
load_path();
load_all_paths();
IupMainLoop();
IupClose();
+78 -96
View File
@@ -6,17 +6,23 @@
#include <stdlib.h>
#include <wchar.h>
// 从注册表加载PATH
void load_path()
// 内部辅助函数:加载单个列表
static void load_single_path(HKEY hKeyRoot, const wchar_t *regPath, Ihandle *list, StringList *cache)
{
// 清空旧缓存
clear_string_list(cache);
HKEY hKey;
LONG res = RegOpenKeyExW(HKEY_LOCAL_MACHINE, REG_PATH, 0, KEY_READ, &hKey);
LONG res = RegOpenKeyExW(hKeyRoot, regPath, 0, KEY_READ, &hKey);
if (res != ERROR_SUCCESS)
{
// 只有 HKLM 失败才提示需要管理员,HKCU 失败可能是其他原因
if (hKeyRoot == HKEY_LOCAL_MACHINE)
{
char msg[512];
snprintf(msg, sizeof(msg), "无法打开注册表键 (HKLM)。\n路径: %ls\n错误码: %ld\n\n请尝试右键点击程序 -> '以管理员身份运行'。", REG_PATH, res);
snprintf(msg, sizeof(msg), "无法打开注册表键 (HKLM)。\n路径: %ls\n错误码: %ld\n\n请尝试右键点击程序 -> '以管理员身份运行'。", regPath, res);
IupMessage("错误", msg);
IupSetAttribute(lbl_status, "TITLE", "状态: 注册表读取失败");
}
return;
}
@@ -24,172 +30,148 @@ void load_path()
res = RegQueryValueExW(hKey, REG_VALUE, NULL, &type, NULL, &size);
if (res == ERROR_SUCCESS)
{
// 安全分配内存:size 是字节数,多分配 2 个字节给 null 终止符
wchar_t *buffer = (wchar_t *)malloc(size + 2);
if (!buffer)
if (buffer)
{
IupMessage("错误", "内存分配失败!");
RegCloseKey(hKey);
return;
}
// 初始化内存
memset(buffer, 0, size + 2);
if (RegQueryValueExW(hKey, REG_VALUE, NULL, &type, (LPBYTE)buffer, &size) == ERROR_SUCCESS)
{
// 重新实现分割逻辑,避免 wcstok 的兼容性问题
wchar_t *current = buffer;
wchar_t *next_semicolon = NULL;
int count = 0;
// 清空列表
IupSetAttribute(list_path, "REMOVEITEM", "ALL");
// 检查内容是否为空
if (wcslen(buffer) == 0)
{
IupMessage("提示", "读取到的 PATH 变量为空!");
}
IupSetAttribute(list, "REMOVEITEM", "ALL");
while (*current)
{
// 查找下一个分号
next_semicolon = wcschr(current, L';');
if (next_semicolon)
{
*next_semicolon = L'\0'; // 临时截断
}
*next_semicolon = L'\0';
// 跳过空项
if (wcslen(current) > 0)
{
char *utf8_str = wide_to_utf8(current);
// 添加到列表
count++;
IupSetAttributeId(list_path, "", count, utf8_str);
IupSetAttributeId(list, "", count, utf8_str);
// 添加到缓存
add_string_list(cache, utf8_str);
free(utf8_str);
}
if (next_semicolon)
{
current = next_semicolon + 1;
}
else
{
break;
}
}
IupSetInt(list_path, "COUNT", count); // 显式设置列表项数量
IupSetInt(list_path, "VALUE", 1); // 选中第一项
// 刷新斑马纹样式
refresh_list_style();
char status_msg[100];
sprintf(status_msg, "状态: 已加载 %d 个条目", count);
IupSetAttribute(lbl_status, "TITLE", status_msg);
}
else
{
IupMessage("错误", "读取 PATH 值内容失败!");
IupSetAttribute(lbl_status, "TITLE", "状态: 读取值内容失败");
IupSetInt(list, "COUNT", count);
IupSetInt(list, "VALUE", 1);
}
free(buffer);
}
else
{
char msg[256];
sprintf(msg, "查询 PATH 值大小失败。错误码: %ld", res);
IupMessage("错误", msg);
IupSetAttribute(lbl_status, "TITLE", "状态: 查询值失败");
}
RegCloseKey(hKey);
}
// 保存PATH到注册表
void save_path()
// 加载所有PATH
void load_all_paths()
{
if (!check_admin())
{
IupMessage("错误", "需要管理员权限才能保存更改!\n请重新以管理员身份运行程序。");
return;
load_single_path(HKEY_LOCAL_MACHINE, REG_PATH_SYS, list_sys, &raw_sys_paths);
load_single_path(HKEY_CURRENT_USER, REG_PATH_USER, list_user, &raw_user_paths);
refresh_list_style();
IupSetAttribute(lbl_status, "TITLE", "状态: 已加载系统和用户变量");
}
int count = IupGetInt(list_path, "COUNT");
if (count == 0)
// 内部辅助函数:保存单个列表
static int save_single_path(HKEY hKeyRoot, const wchar_t *regPath, Ihandle *list)
{
// 警告:清空PATH是很危险的
if (IupAlarm("警告", "PATH 为空!这可能导致系统命令无法使用。\n确定要保存吗?", "确定", "取消", NULL) != 1)
{
return;
}
}
int count = IupGetInt(list, "COUNT");
// 计算所需缓冲区大小
// 计算大小
size_t total_len = 0;
for (int i = 1; i <= count; i++)
{
char *item = IupGetAttributeId(list_path, "", i);
char *item = IupGetAttributeId(list, "", i);
if (item)
{
wchar_t *witem = utf8_to_wide(item);
total_len += wcslen(witem) + 1; // +1 for ';'
total_len += wcslen(witem) + 1;
free(witem);
}
}
total_len += 1; // null terminator
total_len += 1;
wchar_t *buffer = (wchar_t *)malloc(total_len * sizeof(wchar_t));
if (!buffer)
{
IupMessage("错误", "内存分配失败 (保存时)");
return;
}
buffer[0] = L'\0';
return 0;
buffer[0] = L'\0';
for (int i = 1; i <= count; i++)
{
char *item = IupGetAttributeId(list_path, "", i);
char *item = IupGetAttributeId(list, "", i);
if (item)
{
wchar_t *witem = utf8_to_wide(item);
wcscat(buffer, witem);
if (i < count)
{
wcscat(buffer, L";");
}
free(witem);
}
}
// 写入注册表
HKEY hKey;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, REG_PATH, 0, KEY_WRITE, &hKey) == ERROR_SUCCESS)
int success = 0;
if (RegOpenKeyExW(hKeyRoot, regPath, 0, KEY_WRITE, &hKey) == ERROR_SUCCESS)
{
// 使用 REG_EXPAND_SZ 类型,因为 PATH 可能包含 %SystemRoot%
DWORD size = (wcslen(buffer) + 1) * sizeof(wchar_t);
if (RegSetValueExW(hKey, REG_VALUE, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size) == ERROR_SUCCESS)
{
// 发送系统广播通知环境变量已更改
SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)L"Environment", SMTO_ABORTIFHUNG, 5000, NULL);
IupMessage("成功", "PATH 环境变量已更新!");
IupSetAttribute(lbl_status, "TITLE", "状态: 保存成功");
}
else
{
IupMessage("错误", "写入注册表失败!");
IupSetAttribute(lbl_status, "TITLE", "状态: 保存失败");
success = 1;
}
RegCloseKey(hKey);
}
else
{
IupMessage("错误", "无法打开注册表进行写入。请检查权限!");
IupSetAttribute(lbl_status, "TITLE", "状态: 打开注册表失败");
}
free(buffer);
return success;
}
// 保存所有PATH
void save_all_paths()
{
if (!check_admin())
{
IupMessage("错误", "需要管理员权限才能保存更改!");
return;
}
// 备份
backup_registry();
int sys_ok = save_single_path(HKEY_LOCAL_MACHINE, REG_PATH_SYS, list_sys);
int user_ok = save_single_path(HKEY_CURRENT_USER, REG_PATH_USER, list_user);
if (sys_ok && user_ok)
{
SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)L"Environment", SMTO_ABORTIFHUNG, 5000, NULL);
IupMessage("成功", "系统和用户 PATH 环境变量均已更新!");
IupSetAttribute(lbl_status, "TITLE", "状态: 全部保存成功");
}
else if (sys_ok)
{
IupMessage("提示", "系统变量保存成功,但用户变量保存失败。");
}
else if (user_ok)
{
IupMessage("提示", "用户变量保存成功,但系统变量保存失败。");
}
else
{
IupMessage("错误", "保存失败!");
IupSetAttribute(lbl_status, "TITLE", "状态: 保存失败");
}
}
+180 -8
View File
@@ -3,6 +3,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <iup.h>
#include <time.h>
#include <direct.h>
#include <string.h>
// 宽字符转UTF-8 (用于IUP显示)
char *wide_to_utf8(const wchar_t *wstr)
@@ -39,23 +42,192 @@ int check_admin()
return 0;
}
// 刷新列表样式(斑马纹)
void refresh_list_style()
// 检查路径是否存在
static int path_exists(const char *path)
{
if (!list_path)
// 如果包含 %,说明是变量,无法直接检查存在性,默认视为有效
if (strchr(path, '%'))
return 1;
wchar_t *wpath = utf8_to_wide(path);
if (!wpath)
return 0;
DWORD attr = GetFileAttributesW(wpath);
free(wpath);
if (attr == INVALID_FILE_ATTRIBUTES)
return 0;
return (attr & FILE_ATTRIBUTE_DIRECTORY); // 必须是目录
}
// 检查路径是否存在(公开给外部使用)
int is_path_valid(const char *path)
{
return path_exists(path);
}
// 刷新列表样式(斑马纹 + 有效性检查)
void refresh_single_list_style(Ihandle *list)
{
if (!list)
return;
int count = IupGetInt(list_path, "COUNT");
int count = IupGetInt(list, "COUNT");
// 用于查重的简单数组(实际项目可以用哈希表)
// 为了简单,我们只用双重循环检查重复,性能在几百个条目下没问题
for (int i = 1; i <= count; i++)
{
// 奇数行:白色
// 偶数行:极浅灰色 (245 245 245)
char *item = IupGetAttributeId(list, "", i);
if (!item)
continue;
// 默认颜色:黑字
char fg_color[32] = "0 0 0";
// 1. 检查有效性
if (!path_exists(item))
{
// 无效路径:红色
sprintf(fg_color, "255 0 0");
}
// 2. 检查重复 (只检查当前项之前的项,如果之前出现过,当前项标橙)
for (int j = 1; j < i; j++)
{
char *prev_item = IupGetAttributeId(list, "", j);
if (prev_item && _stricmp(item, prev_item) == 0) // Windows 路径不区分大小写
{
// 重复路径:橙色
sprintf(fg_color, "255 128 0");
break;
}
}
IupSetAttributeId(list, "ITEMFGCOLOR", i, fg_color);
// 斑马纹背景
if (i % 2 == 0)
{
IupSetAttributeId(list_path, "ITEMBGCOLOR", i, "245 245 245");
IupSetAttributeId(list, "ITEMBGCOLOR", i, "245 245 245");
}
else
{
IupSetAttributeId(list_path, "ITEMBGCOLOR", i, "255 255 255");
IupSetAttributeId(list, "ITEMBGCOLOR", i, "255 255 255");
}
}
}
// 刷新所有列表样式
void refresh_list_style()
{
refresh_single_list_style(list_sys);
refresh_single_list_style(list_user);
}
// 备份注册表
void backup_registry()
{
// 创建 records 目录
if (_mkdir("records") == -1)
{
// 目录可能已存在,忽略错误
}
// 获取当前时间
time_t t = time(NULL);
struct tm *tm_info = localtime(&t);
char time_str[64];
strftime(time_str, sizeof(time_str), "%Y%m%d_%H%M%S", tm_info);
// 构造备份文件名
char filename[256];
snprintf(filename, sizeof(filename), "records\\backup_%s.reg", time_str);
// 构造 reg export 命令
char full_cmd[1024];
snprintf(full_cmd, sizeof(full_cmd), "reg.exe export \"HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment\" \"%s\" /y", filename);
// 使用 CreateProcess 隐藏窗口执行
STARTUPINFOA si;
PROCESS_INFORMATION pi;
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE; // 隐藏窗口
memset(&pi, 0, sizeof(pi));
if (CreateProcessA(NULL, full_cmd, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
{
// 等待进程结束
WaitForSingleObject(pi.hProcess, INFINITE);
DWORD exit_code = 0;
GetExitCodeProcess(pi.hProcess, &exit_code);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
}
// 初始化字符串列表
void init_string_list(StringList *list)
{
list->items = NULL;
list->count = 0;
list->capacity = 0;
}
// 不区分大小写的字符串查找 (简单实现)
char *stristr(const char *haystack, const char *needle)
{
if (!haystack || !needle)
return NULL;
if (*needle == '\0')
return (char *)haystack;
const char *h = haystack;
const char *n = needle;
const char *h_start = haystack;
while (*h)
{
h_start = h;
n = needle;
while (*h && *n && (tolower((unsigned char)*h) == tolower((unsigned char)*n)))
{
h++;
n++;
}
if (*n == '\0')
return (char *)h_start;
h = h_start + 1;
}
return NULL;
}
// 添加字符串到列表
void add_string_list(StringList *list, const char *str)
{
if (list->count >= list->capacity)
{
list->capacity = (list->capacity == 0) ? 16 : list->capacity * 2;
list->items = (char **)realloc(list->items, list->capacity * sizeof(char *));
}
list->items[list->count] = _strdup(str); // 复制字符串
list->count++;
}
// 清空字符串列表
void clear_string_list(StringList *list)
{
for (int i = 0; i < list->count; i++)
{
free(list->items[i]);
}
free(list->items);
list->items = NULL;
list->count = 0;
list->capacity = 0;
}