diff --git a/Makefile b/Makefile index 870ecc1..4b160c3 100644 --- a/Makefile +++ b/Makefile @@ -18,9 +18,9 @@ CFLAGS = -Wall -O2 -I$(INCLUDE_DIR) -I$(LOCAL_INCLUDE_DIR) -D_WIN32 -DUNICODE -D LDFLAGS = -L$(LIB_DIR) -liup -liupcd -lgdi32 -lcomdlg32 -lcomctl32 -luuid -lole32 -ladvapi32 -mwindows # Source -SRC = src/main.c src/utils.c src/registry.c src/callbacks.c +SRC = src/main.c src/utils.c src/registry.c src/callbacks.c src/ui.c RES = ico/resources.rc -OBJ = $(OBJ_DIR)/main.o $(OBJ_DIR)/utils.o $(OBJ_DIR)/registry.o $(OBJ_DIR)/callbacks.o $(OBJ_DIR)/resources.o +OBJ = $(OBJ_DIR)/main.o $(OBJ_DIR)/utils.o $(OBJ_DIR)/registry.o $(OBJ_DIR)/callbacks.o $(OBJ_DIR)/ui.o $(OBJ_DIR)/resources.o EXE = $(BIN_DIR)/PathEditor.exe all: $(BIN_DIR) $(OBJ_DIR) $(EXE) @@ -46,6 +46,9 @@ $(OBJ_DIR)/registry.o: src/registry.c $(OBJ_DIR)/callbacks.o: src/callbacks.c $(CC) $(CFLAGS) -c -o $@ $< +$(OBJ_DIR)/ui.o: src/ui.c + $(CC) $(CFLAGS) -c -o $@ $< + $(OBJ_DIR)/resources.o: ico/resources.rc $(WINDRES) -i $< -o $@ diff --git a/README.md b/README.md index 48e8c1a..f97f6f9 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,36 @@ # Path Editor (系统环境变量编辑器) -一个简单、轻量级的 Windows 系统环境变量(PATH)编辑器,基于 C 语言和 IUP 图形库开发。 +一个简单、轻量级但功能强大的 Windows 系统环境变量(PATH)编辑器,基于 C 语言和 IUP 图形库开发。 ## ✨ 功能特点 -* **可视化编辑**:直观地查看和管理系统 PATH 环境变量。 -* **安全操作**:必须以管理员身份运行才能保存更改,防止误操作。 +* **🛡️ 安全第一**: + * **自动备份**:每次保存前自动备份注册表,防止意外。 + * **只读模式**:非管理员运行时自动切换到只读模式,防止误操作。 + * **权限检测**:智能检测当前运行权限。 + +* **📑 双视图管理**: + * 完美支持 **System (系统变量)** 和 **User (用户变量)** 的分离查看与编辑。 + * 清晰的 Tab 标签页切换。 + +* **🔴 智能诊断与维护**: + * **无效路径高亮**:自动检测路径是否存在,不存在的显示为红色。 + * **重复路径高亮**:自动检测重复项,重复的显示为橙色。 + * **一键清理**:智能移除所有无效和重复的路径,保持环境整洁。 + +* **📂 高效交互**: + * **拖拽支持**:直接将文件夹拖入窗口即可添加(支持管理员模式下的 UIPI 穿透)。 + * **实时搜索**:顶部搜索框支持不区分大小写的实时过滤查找。 + * **快捷键**:支持 Delete 键快速删除选中项。 + * **便捷管理**: * ➕ **新建**:添加新路径到列表。 * 📂 **浏览**:直接从文件资源管理器选择目录添加。 - * ✏️ **编辑**:修改现有路径。 + * ✏️ **编辑**:双击或点击按钮修改现有路径。 * 🗑️ **删除**:移除不需要的路径。 * ⬆️⬇️ **排序**:上移/下移调整路径优先级。 -* **轻量级**:原生 C 语言编写,运行速度快,占用资源少。 + +* **轻量级**:原生 C 语言编写,无臃肿依赖,运行速度极快。 ## 📦 下载与安装 @@ -30,6 +48,7 @@ * GCC 编译器 (推荐 MinGW-w64) * Make 工具 * IUP 库 (已包含在 `libs` 目录下) +* Inno Setup 6 (仅打包需要) ### 编译步骤 @@ -54,16 +73,22 @@ 本项目使用 Inno Setup 生成安装包。 1. 确保已安装 [Inno Setup 6](https://jrsoftware.org/isdl.php)。 -2. 编译项目生成 exe 文件。 -3. 使用 Inno Setup 编译 `dist/installer.iss` 脚本。 +2. 运行根目录下的 `build_installer.bat` 脚本。 +3. 生成的安装包将位于 `dist/dist/PathEditorSetup.exe`。 ## 📝 使用说明 1. **启动**:右键点击程序图标,选择“以管理员身份运行”。 2. **查看**:程序启动后会自动加载当前的系统 PATH 变量。 -3. **修改**:使用右侧按钮栏进行添加、删除、移动等操作。 -4. **保存**:操作完成后,务必点击底部的【确定】按钮保存更改。 -5. **生效**:保存后,某些正在运行的程序可能需要重启才能识别新的环境变量。CMD 或 PowerShell 窗口需要重新打开。 + * **红色**条目表示路径不存在。 + * **橙色**条目表示路径重复。 +3. **搜索**:在顶部输入关键词可快速筛选。 +4. **修改**: + * 拖拽文件夹到列表可直接添加。 + * 使用右侧按钮栏进行常规操作。 + * 点击“一键清理”可自动删除无效和重复项。 +5. **保存**:操作完成后,务必点击底部的【确定】按钮保存更改。 +6. **生效**:保存后,某些正在运行的程序可能需要重启才能识别新的环境变量。CMD 或 PowerShell 窗口需要重新打开。 ## 👤 作者信息 diff --git a/bin/PathEditor.exe b/bin/PathEditor.exe index da67560..0bce9f1 100644 Binary files a/bin/PathEditor.exe and b/bin/PathEditor.exe differ diff --git a/bin/records/backup_20260316_193937.reg b/bin/records/backup_20260316_193937.reg deleted file mode 100644 index 02da0a9..0000000 Binary files a/bin/records/backup_20260316_193937.reg and /dev/null differ diff --git a/bin/records/backup_20260316_194700.reg b/bin/records/backup_20260316_194700.reg deleted file mode 100644 index 02da0a9..0000000 Binary files a/bin/records/backup_20260316_194700.reg and /dev/null differ diff --git a/bin/records/backup_20260316_195033.reg b/bin/records/backup_20260316_195033.reg deleted file mode 100644 index 02da0a9..0000000 Binary files a/bin/records/backup_20260316_195033.reg and /dev/null differ diff --git a/bin/records/backup_20260316_195233.reg b/bin/records/backup_20260316_195233.reg deleted file mode 100644 index 02da0a9..0000000 Binary files a/bin/records/backup_20260316_195233.reg and /dev/null differ diff --git a/bin/records/backup_20260316_195238.reg b/bin/records/backup_20260316_195238.reg deleted file mode 100644 index 02da0a9..0000000 Binary files a/bin/records/backup_20260316_195238.reg and /dev/null differ diff --git a/bin/records/backup_20260316_195622.reg b/bin/records/backup_20260316_195622.reg deleted file mode 100644 index 02da0a9..0000000 Binary files a/bin/records/backup_20260316_195622.reg and /dev/null differ diff --git a/build_installer.bat b/build_installer.bat new file mode 100644 index 0000000..240ea14 --- /dev/null +++ b/build_installer.bat @@ -0,0 +1,15 @@ +@echo off +echo Copying DLLs... +if not exist bin mkdir bin +copy /Y "libs\iup-3.31_Win64_dllw6_lib\*.dll" bin\ + +echo Building Installer... +"D:\Program Files (x86)\Inno Setup 6\ISCC.exe" "dist\installer.iss" + +if %ERRORLEVEL% NEQ 0 ( + echo Installer build failed! + exit /b %ERRORLEVEL% +) + +echo Done! Installer is in dist\dist\ +pause \ No newline at end of file diff --git a/include/ui.h b/include/ui.h new file mode 100644 index 0000000..e894714 --- /dev/null +++ b/include/ui.h @@ -0,0 +1,15 @@ +#ifndef UI_H +#define UI_H + +#include + +// 创建列表控件 +Ihandle *create_path_list(); + +// 创建右侧功能按钮区域 +Ihandle *create_main_buttons(); + +// 创建底部按钮区域 +Ihandle *create_bottom_buttons(); + +#endif // UI_H \ No newline at end of file diff --git a/src/main.c b/src/main.c index fa1afbf..b72ff57 100644 --- a/src/main.c +++ b/src/main.c @@ -7,33 +7,27 @@ #include "utils.h" #include "registry.h" #include "callbacks.h" +#include "ui.h" + +// 定义 Windows 消息常量 +#ifndef WM_COPYGLOBALDATA +#define WM_COPYGLOBALDATA 0x0049 +#endif + +#ifndef MSGFLT_ADD +#define MSGFLT_ADD 1 +#endif // 全局变量定义 StringList raw_sys_paths = {0}; StringList raw_user_paths = {0}; // 全局控件定义 -Ihandle *dlg, *tabs_main, *list_sys, *list_user, *lbl_status; // 主窗口、标签页、系统路径列表、用户路径列表、状态标签 +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; -} +Ihandle *btn_ok, *btn_cancel, *btn_help; // 确认取消帮助按钮 +Ihandle *btn_clean; // 一键清理按钮 +Ihandle *txt_search; // 搜索框 // 主函数 int main(int argc, char **argv) @@ -44,6 +38,24 @@ int main(int argc, char **argv) IupOpen(&argc, &argv); IupSetGlobal("UTF8MODE", "YES"); + // 在管理员模式下,解决无法拖拽文件到列表框的问题 (UIPI) + // 需要加载 User32.dll 获取 ChangeWindowMessageFilter 函数 + HMODULE hUser32 = LoadLibraryW(L"user32.dll"); + if (hUser32) + { + typedef BOOL(WINAPI * ChangeWindowMessageFilterProc)(UINT, DWORD); + ChangeWindowMessageFilterProc pChangeWindowMessageFilter = + (ChangeWindowMessageFilterProc)GetProcAddress(hUser32, "ChangeWindowMessageFilter"); + + if (pChangeWindowMessageFilter) + { + pChangeWindowMessageFilter(WM_DROPFILES, MSGFLT_ADD); + pChangeWindowMessageFilter(WM_COPYDATA, MSGFLT_ADD); + pChangeWindowMessageFilter(WM_COPYGLOBALDATA, MSGFLT_ADD); + } + FreeLibrary(hUser32); + } + // 创建两个列表控件 list_sys = create_path_list(); list_user = create_path_list(); @@ -63,42 +75,8 @@ int main(int argc, char **argv) IupSetAttribute(tabs_main, "TABTITLE1", "用户变量 (User)"); IupSetAttribute(tabs_main, "TABTYPE", "TOP"); - // 创建右侧按钮 - btn_new = IupButton("新建(N)", NULL); - btn_edit = IupButton("编辑(E)", NULL); - btn_browse = IupButton("浏览(B)...", NULL); - 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); - IupSetCallback(btn_edit, "ACTION", (Icallback)btn_edit_cb); - IupSetCallback(btn_browse, "ACTION", (Icallback)btn_browse_cb); - 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(btn_clean, "ACTION", (Icallback)btn_clean_cb); - - // 设置按钮大小 (宽度和高度都增加约1/4) - IupSetAttribute(btn_new, "RASTERSIZE", "100x32"); - IupSetAttribute(btn_edit, "RASTERSIZE", "100x32"); - IupSetAttribute(btn_browse, "RASTERSIZE", "100x32"); - 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 *vbox_btns = create_main_buttons(); // 上部布局:Tabs + 按钮 Ihandle *hbox_main = IupHbox(tabs_main, vbox_btns, NULL); @@ -109,23 +87,8 @@ int main(int argc, char **argv) lbl_status = IupLabel("状态: 就绪"); IupSetAttribute(lbl_status, "EXPAND", "HORIZONTAL"); - // 底部按钮 - btn_ok = IupButton("确定", NULL); - btn_cancel = IupButton("取消", NULL); - btn_help = IupButton("帮助(?)", NULL); - - IupSetCallback(btn_ok, "ACTION", (Icallback)btn_ok_cb); - IupSetCallback(btn_cancel, "ACTION", (Icallback)btn_cancel_cb); - IupSetCallback(btn_help, "ACTION", (Icallback)btn_help_cb); - - IupSetAttribute(btn_ok, "RASTERSIZE", "100x32"); - IupSetAttribute(btn_cancel, "RASTERSIZE", "100x32"); - IupSetAttribute(btn_help, "RASTERSIZE", "100x32"); - - Ihandle *hbox_bottom = IupHbox(lbl_status, IupFill(), btn_help, btn_ok, btn_cancel, NULL); - IupSetAttribute(hbox_bottom, "GAP", "10"); - IupSetAttribute(hbox_bottom, "MARGIN", "10x10"); - IupSetAttribute(hbox_bottom, "ALIGNMENT", "ACENTER"); + // 创建底部按钮区域 + Ihandle *hbox_bottom = create_bottom_buttons(); // 总体布局 Ihandle *vbox_all = IupVbox( diff --git a/src/ui.c b/src/ui.c new file mode 100644 index 0000000..ddfbe94 --- /dev/null +++ b/src/ui.c @@ -0,0 +1,87 @@ +#include "ui.h" +#include "globals.h" +#include "callbacks.h" +#include + +// 创建列表控件 +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; +} + +// 创建右侧功能按钮区域 +Ihandle *create_main_buttons() +{ + // 创建右侧按钮 + btn_new = IupButton("新建(N)", NULL); + btn_edit = IupButton("编辑(E)", NULL); + btn_browse = IupButton("浏览(B)...", NULL); + 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); + IupSetCallback(btn_edit, "ACTION", (Icallback)btn_edit_cb); + IupSetCallback(btn_browse, "ACTION", (Icallback)btn_browse_cb); + 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(btn_clean, "ACTION", (Icallback)btn_clean_cb); + + // 设置按钮大小 (宽度和高度都增加约1/4) + IupSetAttribute(btn_new, "RASTERSIZE", "100x32"); + IupSetAttribute(btn_edit, "RASTERSIZE", "100x32"); + IupSetAttribute(btn_browse, "RASTERSIZE", "100x32"); + 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"); + + return vbox_btns; +} + +// 创建底部按钮区域 +Ihandle *create_bottom_buttons() +{ + // 底部按钮 + btn_ok = IupButton("确定", NULL); + btn_cancel = IupButton("取消", NULL); + btn_help = IupButton("帮助(?)", NULL); + + IupSetCallback(btn_ok, "ACTION", (Icallback)btn_ok_cb); + IupSetCallback(btn_cancel, "ACTION", (Icallback)btn_cancel_cb); + IupSetCallback(btn_help, "ACTION", (Icallback)btn_help_cb); + + IupSetAttribute(btn_ok, "RASTERSIZE", "100x32"); + IupSetAttribute(btn_cancel, "RASTERSIZE", "100x32"); + IupSetAttribute(btn_help, "RASTERSIZE", "100x32"); + + Ihandle *hbox_bottom = IupHbox(lbl_status, IupFill(), btn_help, btn_ok, btn_cancel, NULL); + IupSetAttribute(hbox_bottom, "GAP", "10"); + IupSetAttribute(hbox_bottom, "MARGIN", "10x10"); + IupSetAttribute(hbox_bottom, "ALIGNMENT", "ACENTER"); + + return hbox_bottom; +} \ No newline at end of file