Compare commits

...

29 Commits

Author SHA1 Message Date
Serendipity 06aee13f55 docs: 更新CLAUDE.md以匹配v2.6 MVC架构
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-15 07:42:35 +08:00
Serendipity 9d0797eafa build: 合并v2.6分支,解决CMake构建系统冲突
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-14 22:59:01 +08:00
Serendipity f69787a110 fix: 修复PR#5 review意见
- CMakeLists.txt: 启用RC语言, 移除冗余_WIN32, 添加DLL复制
- CLAUDE.md: 更新构建命令为CMake
- README.md: 移除硬编码MinGW路径和Makefile引用

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-14 22:57:30 +08:00
Serendipity 9a78b88c4a refactor(core): 统一使用ErrorCode并添加错误日志记录
- 将path_manager和import_export模块的返回值从int/void改为ErrorCode
- 在关键操作中添加日志记录,便于调试和错误追踪
- 更新调用代码以检查错误码并记录错误
- 修改运行命令以管理员权限启动程序
2026-03-26 18:27:38 +08:00
Serendipity 3af0e96060 refactor: 移除未使用的布局配置并增强日志与安全字符串
- 删除未使用的 layout_config.h 头文件及其引用
- 在 main.c 和 callbacks.c 中添加日志记录以追踪程序启动、关闭和关键操作
- 将多处 strncpy 调用替换为安全的 safe_strcpy 函数
- 在 dialogs.c 中引入 safe_string.h 以使用安全字符串函数
2026-03-26 13:20:34 +08:00
Serendipity 6ba7e702f2 refactor(registry): 使用ErrorCode类型替换int作为错误返回值
- 将registry_service.h/c中的函数返回值从int改为ErrorCode枚举
- 更新callbacks.c中的错误检查逻辑,使用ERR_OK常量进行比较
- 在内部辅助函数中返回具体的错误码(ERR_REGISTRY_FAILED等)
- 提高代码类型安全性和错误处理可读性
2026-03-26 13:02:14 +08:00
Serendipity 9aa1e208ba docs: 更新 README 中的架构与工具库说明
更新“架构与二次开发”章节的图标,并补充了项目中包含的开发工具库列表,如统一错误码系统、安全字符串函数等,以便开发者更全面地了解项目结构。
2026-03-26 12:50:29 +08:00
Serendipity d934d21323 feat: 新增安全字符串处理和日志系统模块
- 添加 safe_string 模块,提供安全的字符串复制、拼接和复制功能
- 添加 logger 模块,支持多级别日志记录和文件输出
- 添加 error_code 模块,定义统一的错误代码枚举
- 添加 layout_config 模块,定义布局配置结构
- 更新 CMakeLists.txt 包含新增的源文件
2026-03-26 12:47:44 +08:00
Serendipity 8767271e96 feat(导入导出): 支持同时导出和导入系统与用户PATH变量
- 重构导出功能,将系统变量和用户变量合并到单个JSON文件中
- 重构导入功能,支持解析包含系统变量和用户变量的JSON文件
- 在导入时提供选项让用户选择导入目标(系统变量、用户变量或全部)
- 更新UI交互逻辑,适配新的导入导出数据结构
- 改进JSON文件格式,包含版本信息和导出时间戳
2026-03-26 12:16:48 +08:00
Serendipity 55ff64b92d docs: 更新 README 以反映新增的导入导出功能
更新功能列表,新增“导入导出”模块的说明,包括 JSON 备份/恢复和旧版 TXT 格式兼容。
更新架构说明,将“统一配置中心”修改为“热配置系统”,以反映从 C++ 头文件到 Lua 配置文件的变更。
2026-03-25 19:56:10 +08:00
Serendipity 55d0f80743 feat: 新增导入导出功能,支持备份和恢复 PATH 配置
- 添加 import_export 模块,实现 JSON 格式的导入导出
- 在界面中添加导入和导出按钮,并配置回调函数
- 更新配置文件,增加相关文本和状态提示
- 在非管理员权限下禁用导入按钮
2026-03-25 19:49:47 +08:00
Serendipity ce232cb024 feat: 引入 Lua 配置系统实现 UI 参数热更新
- 添加 Lua 5.5 库支持,包含头文件和动态链接库
- 新增 lua_config 模块,提供配置初始化、获取字符串/整型值等功能
- 创建 config.lua 配置文件,集中管理所有 UI 文本、尺寸和布局参数
- 移除原有的硬编码 config.h,将 UI 常量迁移至 Lua 配置
- 修改主窗口、对话框和回调函数,动态读取 Lua 配置值
- 更新 CMakeLists.txt,添加 Lua 库依赖和 DLL 复制步骤
- 删除过时的 Makefile,统一使用 CMake 构建
2026-03-25 19:18:23 +08:00
Serendipity bd1b05be55 chore: 更新运行命令注释以反映最新的可执行文件路径 2026-03-19 22:21:33 +08:00
Serendipity a769a6b9b3 refactor: 重构项目为 MVC 架构并移除全局变量
- 将原有扁平目录结构重构为 MVC 分层架构:
  * src/core/: 核心业务逻辑(Model),完全独立于 UI 框架
  * src/ui/: 界面组件构建(View),负责纯视觉展示
  * src/controller/: 用户交互处理(Controller),连接数据与界面
  * src/utils/: 底层工具函数,专注于系统调用和字符串处理
- 引入 AppContext 结构体统一管理应用状态,替代原有的全局变量模式
- 重命名并重新组织头文件,按功能模块划分到对应子目录
- 更新 CMakeLists.txt 以适配新的目录结构
- 同步更新 README.md 文档,说明新的架构设计
2026-03-19 20:58:41 +08:00
Serendipity 6509ef98e4 chore: 清理旧版IUP库文件并更新头文件路径
删除旧的iup-3.31_Win64_dllw6_lib目录下的所有DLL和静态库文件
将IUP头文件从旧目录迁移到新的libs/IUP/include统一路径
更新CMakeLists.txt中的包含路径和库链接配置
简化DLL复制逻辑,只复制核心iup.dll文件
2026-03-19 20:14:06 +08:00
Serendipity c928c271e8 chore: 移除构建安装程序前的DLL复制步骤
不再需要手动复制IUP DLL文件,因为安装脚本已直接引用库目录。
2026-03-19 13:23:03 +08:00
Serendipity 02e702b285 fix(构建): 修复IUP DLL复制命令的路径变量
将CMAKE_SOURCE_DIR更改为CMAKE_CURRENT_SOURCE_DIR以确保在子目录中也能正确找到DLL文件。
移除不必要的条件判断,使复制命令始终执行。
2026-03-19 12:40:01 +08:00
Serendipity af3138c146 build: 重构 CMakeLists.txt 以使用现代 CMake 最佳实践
- 将项目声明更新为包含版本和语言
- 启用 RC 语言以正确处理资源文件
- 使用 target_* 命令替代全局命令(如 include_directories、link_directories)
- 将资源文件直接加入源文件列表,简化构建定义
- 优化 DLL 复制逻辑,使用 file(GLOB) 和 copy_if_different
- 改进编译器选项的条件设置,增强跨编译器兼容性
2026-03-19 12:37:41 +08:00
Serendipity 6e6adf3b85 chore: 迁移构建系统并清理遗留的二进制文件
- 删除 bin/ 目录下遗留的 DLL 和可执行文件,它们现在由 CMake 构建过程自动复制
- 更新 CMakeLists.txt,明确设置 C17 标准并优化编译选项
- 更新 Inno Setup 安装脚本,使其从 build/ 目录获取构建产物
- 更新 main.c 中的编译说明,反映当前基于 CMake 的构建流程
2026-03-19 12:32:54 +08:00
Serendipity e84b33c5ca build: 迁移项目构建系统至 CMake
- 新增 CMakeLists.txt 文件,定义项目构建规则、依赖和编译选项。
- 更新 README.md 文档,推荐使用 CMake 进行构建,并说明新旧构建方式。
- 保留原有的 Makefile 支持以保持向后兼容。
2026-03-19 12:07:01 +08:00
Serendipity ac6b409f3a feat: 为只读模式添加专用应用程序标题
在非管理员权限下运行时,将对话框标题从硬编码字符串改为使用配置文件中定义的 APP_NAME_READONLY 宏。这提高了代码的可维护性和一致性,使标题文本集中管理,便于未来修改。
2026-03-18 22:37:33 +08:00
Serendipity 1bbe95582a refactor: 将应用程序名称提取为配置常量
- 在 config.h 中定义 APP_NAME 常量,提高可维护性
- 将 main.c 中的对话框标题硬编码替换为使用 APP_NAME
2026-03-18 22:33:25 +08:00
Serendipity 3ecf35963d feat(ui): 增加对话框最小尺寸并调整默认大小
- 将对话框默认大小从500x400调整为800x800
- 添加MINSIZE属性确保对话框不可缩小
- 清理ui.c中的多余空白字符
- 在main.c中添加编译和打包说明注释
2026-03-18 22:03:57 +08:00
Serendipity 276d2c5fe3 docs(config.h): 为UI配置常量添加注释说明 2026-03-18 21:09:13 +08:00
Serendipity a9339f9b9f style(config): 统一宏定义格式并更新文档
调整 config.h 中 UI_DLG_SIZE 宏定义的对齐格式以保持代码风格一致。
在 README.md 中新增“架构与二次开发”章节,说明项目的模块化设计和配置管理方式。
2026-03-18 21:06:55 +08:00
Serendipity 7fac2aab35 refactor: 重构代码以提取配置和全局变量
- 将 Windows 消息常量和 UI 配置常量分别提取到 globals.h 和 config.h 头文件中,提高可维护性
- 将全局变量和控件定义从 main.c 移至独立的 globals.c 源文件,实现关注点分离
- 更新 Makefile 以包含新的源文件 globals.c
- 在 ui.c 和 main.c 中引用 config.h,使用配置常量替代硬编码的 UI 参数
2026-03-18 21:01:50 +08:00
Serendipity 7db190306c docs: 更新 README 项目描述与功能亮点
重写项目概述,使其更具吸引力和信息性。新增对目标用户、核心优势(如双视图、智能检测、自动备份)的说明,以更好地向潜在用户展示工具价值。
2026-03-16 20:32:16 +08:00
Serendipity 575fcca5c4 refactor: 提取UI组件到独立模块并改进拖拽支持
- 将列表、按钮等UI创建代码从main.c移至ui.c/ui.h
- 添加Windows UIPI消息过滤以支持管理员模式下的文件拖拽
- 更新Makefile和构建脚本以包含新的UI模块
- 清理旧的备份注册表文件并更新README文档
2026-03-16 20:15:10 +08:00
Serendipity 39d06e20e0 feat: 新增系统/用户变量分离、搜索、拖拽和清理功能
- 将单一列表拆分为系统和用户变量两个标签页
- 新增搜索框支持实时过滤路径
- 支持拖拽文件夹直接添加到列表
- 新增一键清理功能移除无效和重复路径
- 增加注册表备份机制和删除确认
- 优化界面布局和权限提示逻辑
2026-03-16 19:58:41 +08:00
130 changed files with 4435 additions and 943 deletions
+41 -28
View File
@@ -4,51 +4,64 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## 项目概述
Path Editor 是一个 Windows 系统环境变量 PATH 编辑器,使用 C 语言和 IUP 图形库开发。通过读写注册表 `HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\Path` 来管理系统 PATH
Path Editor 是一个 Windows 系统环境变量PATH)管理工具,使用 C 语言和 IUP 图形库开发。支持系统变量和用户变量的双视图编辑、智能路径检测、自动备份、JSON 导入导出等功能
## 构建与运行
```bash
# 编译(需要 MinGW-w64 在 PATH 中)
mingw32-make
# 配置(需要 MinGW-w64 和 CMake 在 PATH 中)
cmake -B build -G "MinGW Makefiles"
# 清理构建产物
mingw32-make clean
# 编译
cmake --build build
# 运行(必须以管理员身份)
bin/PathEditor.exe
# 打包
build_installer.bat
# 以管理员身份运行
powershell -Command "Start-Process 'build\\PathEditor.exe' -Verb RunAs"
```
编译器为 MinGW-w64 GCC使用 `windres` 编译资源文件(图标)。IUP 3.31 库已包含在 `libs/` 目录下,无需额外安装
## 打包
使用 Inno Setup 6 生成安装包。先编译生成 exe,再用 Inno Setup 编译 `dist/installer.iss`
编译器为 MinGW-w64 GCCC17 标准。IUP 3.31 和 Lua 5.5 库已包含在 `libs/` 目录下。CMake 在 POST_BUILD 阶段自动复制运行时 DLL 到输出目录
## 架构
项目采用 **MVC 分层架构**
```
src/
main.c 入口,构建 UI 布局(IupDialog + IupFlatList + 按钮)
callbacks.c 所有按钮回调 + 自定义输入对话框
registry.c 注册表读写:load_path() / save_path()
utils.c 编码转换 + 管理员权限检测 + 斑马纹刷新
include/
globals.h 全局 Ihandle* 指针和注册表路径常量
callbacks.h 回调函数声明
registry.h 注册表操作声明
utils.h 工具函数声明
├── main.c # 入口:初始化日志/Lua/UI,创建 AppContext
├── core/ # Model — 业务逻辑,零 IUP 依赖
│ ├── registry_service.c # Windows 注册表读写
│ ├── path_manager.c # 路径增删移查与清理
│ ├── app_context.c # 应用运行时状态(StringList sys/user
│ ├── import_export.c # JSON/TXT 导入导出
│ └── lua_config.c # Lua 配置热加载
├── ui/ # View — IUP 界面构建
│ ├── main_window.c # 主窗口布局(Tab、列表、按钮、状态栏)
│ ├── dialogs.c # 自定义输入对话框
│ └── ui_utils.c # 斑马纹刷新等界面工具
├── controller/ # Controller — 连接 UI 与 Model
│ └── callbacks.c # 所有按钮/搜索/拖拽/键盘回调
└── utils/ # 纯工具层,无业务依赖
├── string_ext.c # StringList 动态字符串数组
├── os_env.c # 编码转换 + 管理员权限检测
├── safe_string.c # 安全字符串操作
├── logger.c # 日志系统
└── error_code.h # 统一 ErrorCode 枚举
```
**数据流**: `load_path()`注册表 → 列表) → 用户操作(按钮回调) → `save_path()`(列表 → 注册表 → `WM_SETTINGCHANGE` 广播
**数据流**: `registry_service`注册表加载`AppContext` 持有 StringList → UI 展示 → 用户操作经 `callbacks` 调用 `path_manager` 修改 StringList → `registry_service` 写回注册表 → `WM_SETTINGCHANGE` 广播
**编码约定**: IUP 使用 UTF-8Windows 注册表 API 使用 UTF-16 (wide char)。`utils.c` 提供 `wide_to_utf8()``utf8_to_wide()` 完成转换
**UI 控件寻址**:控件通过 `IupSetHandle("NAME", handle)` 注册名称,通过 `IupGetHandle("NAME")` `IupGetDialogChild(dlg, "NAME")` 查找,不使用全局变量。AppContext 通过 `IupSetAttribute(dlg, "APP_CONTEXT", ctx)` 挂载到对话框上
**全局控件**: 所有 IUP 控件句柄(`dlg`, `list_path`, `lbl_status`, 各按钮)在 `main.c` 定义并通过 `globals.h` 声明为 `extern`,回调函数和工具函数直接访问它们
**Lua 配置**`lua/config.lua` 定义所有 UI 文本和布局参数(按钮文字、颜色、尺寸等),通过 `lua_config_get_string("section", "key")` 读取,修改后无需重新编译
## 关键约束
- 必须以管理员身份运行才能保存更改(`check_admin()` 通过尝试以 `KEY_WRITE` 打开注册表来检测)
- IUP 的 `UTF8MODE` 必须在 `IupOpen()` 之前通过 `putenv("IUP_UTF8MODE=YES")` 设置
- `IupFlatList` 的数据操作(`APPEND` 等属性)必须在控件 Map 之后(`IupShowXY` 之后)才能生效
- PATH 使用 `REG_EXPAND_SZ` 类型以支持 `%SystemRoot%` 等变量展开
- IUP UTF8MODE 必须在 `IupOpen()` 之前通过 `putenv("IUP_UTF8MODE=YES")` 设置
- `IupFlatList` 数据操作必须在 `IupShowXY`(控件 Map)之后才能生效
- 管理员权限检测通过尝试以 KEY_WRITE 打开注册表键实现
- 非管理员模式下所有修改按钮和保存按钮被禁用
- PATH 注册表值使用 `REG_EXPAND_SZ` 类型,支持 `%SystemRoot%` 等变量展开
- 拖拽支持在管理员模式下需要调用 `ChangeWindowMessageFilter` 绕过 UIPI
+82 -21
View File
@@ -1,33 +1,94 @@
cmake_minimum_required(VERSION 3.15)
project(PathEditor LANGUAGES C)
cmake_minimum_required(VERSION 3.10)
project(PathEditor VERSION 3.0 LANGUAGES C)
set(IUP_DIR "${CMAKE_SOURCE_DIR}/libs/iup-3.31_Win64_dllw6_lib")
# 启用资源编译器以处理 .rc 文件
enable_language(RC)
add_executable(PathEditor
# 设置 C 标准
set(CMAKE_C_STANDARD 17)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF) # 禁用特定编译器的扩展(如 gnu17),强制使用标准 C17
# 定义源文件
set(SOURCES
src/main.c
src/utils.c
src/registry.c
src/callbacks.c
src/utils/string_ext.c
src/utils/os_env.c
src/utils/safe_string.c
src/utils/logger.c
src/ui/ui_utils.c
src/ui/dialogs.c
src/ui/main_window.c
src/core/registry_service.c
src/core/path_manager.c
src/core/app_context.c
src/core/lua_config.c
src/core/import_export.c
src/controller/callbacks.c
ico/resources.rc
)
target_include_directories(PathEditor PRIVATE
${IUP_DIR}/include
include
# 创建 GUI 可执行文件(WIN32 属性会自动添加 -mwindows 参数)
add_executable(${PROJECT_NAME} WIN32 ${SOURCES})
# 添加宏定义
target_compile_definitions(${PROJECT_NAME} PRIVATE
_WIN32
UNICODE
_UNICODE
)
target_link_directories(PathEditor PRIVATE ${IUP_DIR})
# 添加编译选项
if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang")
target_compile_options(${PROJECT_NAME} PRIVATE
-Wall
-O2
-fexec-charset=UTF-8
)
endif()
target_link_libraries(PathEditor
iup iupcd gdi32 comdlg32 comctl32 uuid ole32 advapi32
# 设置头文件搜索路径
target_include_directories(${PROJECT_NAME} PRIVATE
${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/include/core
${CMAKE_SOURCE_DIR}/include/ui
${CMAKE_SOURCE_DIR}/include/controller
${CMAKE_SOURCE_DIR}/include/utils
${CMAKE_SOURCE_DIR}/libs/IUP/include
${CMAKE_SOURCE_DIR}/libs/lua/include
)
target_compile_definitions(PathEditor PRIVATE _WIN32 UNICODE _UNICODE)
target_compile_options(PathEditor PRIVATE -Wall -O2 -fexec-charset=UTF-8)
target_link_options(PathEditor PRIVATE -mwindows)
set_target_properties(PathEditor PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/bin"
# 设置库文件搜索路径
target_link_directories(${PROJECT_NAME} PRIVATE
${CMAKE_SOURCE_DIR}/libs/IUP
${CMAKE_SOURCE_DIR}/libs/lua
)
# 链接所需库
target_link_libraries(${PROJECT_NAME} PRIVATE
lua55
iup
iupcd
gdi32
comdlg32
comctl32
uuid
ole32
advapi32
)
# 编译完成后,复制程序实际需要的核心 DLL 文件到构建输出目录
set(IUP_REQUIRED_DLLS "${CMAKE_CURRENT_SOURCE_DIR}/libs/IUP/iup.dll")
set(LUA_REQUIRED_DLLS "${CMAKE_CURRENT_SOURCE_DIR}/libs/lua/lua55.dll")
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${IUP_REQUIRED_DLLS}
"$<TARGET_FILE_DIR:${PROJECT_NAME}>"
COMMENT "Copying required DLLs to build directory..."
)
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${LUA_REQUIRED_DLLS}
"$<TARGET_FILE_DIR:${PROJECT_NAME}>"
COMMENT "Copying Lua DLL to build directory..."
)
-54
View File
@@ -1,54 +0,0 @@
CC = gcc
WINDRES = windres
# Paths - specific to user environment
IUP_DIR = libs/iup-3.31_Win64_dllw6_lib
INCLUDE_DIR = $(IUP_DIR)/include
LIB_DIR = $(IUP_DIR)
LOCAL_INCLUDE_DIR = include
# Output Directories
OBJ_DIR = obj
BIN_DIR = bin
# Flags
# -mwindows: Create GUI app (no console)
# -DUNICODE -D_UNICODE: Use Wide Character API
CFLAGS = -Wall -O2 -I$(INCLUDE_DIR) -I$(LOCAL_INCLUDE_DIR) -D_WIN32 -DUNICODE -D_UNICODE -fexec-charset=UTF-8
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
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
EXE = $(BIN_DIR)/PathEditor.exe
all: $(BIN_DIR) $(OBJ_DIR) $(EXE)
$(BIN_DIR):
if not exist $(BIN_DIR) mkdir $(BIN_DIR)
$(OBJ_DIR):
if not exist $(OBJ_DIR) mkdir $(OBJ_DIR)
$(EXE): $(OBJ)
$(CC) -o $@ $^ $(LDFLAGS)
$(OBJ_DIR)/main.o: src/main.c
$(CC) $(CFLAGS) -c -o $@ $<
$(OBJ_DIR)/utils.o: src/utils.c
$(CC) $(CFLAGS) -c -o $@ $<
$(OBJ_DIR)/registry.o: src/registry.c
$(CC) $(CFLAGS) -c -o $@ $<
$(OBJ_DIR)/callbacks.o: src/callbacks.c
$(CC) $(CFLAGS) -c -o $@ $<
$(OBJ_DIR)/resources.o: ico/resources.rc
$(WINDRES) -i $< -o $@
clean:
if exist $(OBJ_DIR)\*.o del /Q $(OBJ_DIR)\*.o
if exist $(BIN_DIR)\*.exe del /Q $(BIN_DIR)\*.exe
+101 -89
View File
@@ -1,123 +1,135 @@
# Path Editor — Windows 系统环境变量编辑器
# Path Editor (系统环境变量编辑器)
一个轻量级的 Windows 系统环境变量(PATH编辑器,使用 C 语言和 [IUP](https://www.tecgraf.puc-rio.br/iup/) 图形库开发
* Path Editor 是一个专为 Windows 用户设计的系统环境变量(PATH管理工具。它基于原生 C 语言和 IUP 图形库开发,旨在替代 Windows 自带的简陋编辑界面
* 相比系统自带的编辑器,Path Editor 提供了更加直观的双视图(系统/用户变量)界面、智能的路径有效性检测、自动备份机制以及便捷的拖拽操作,让环境变量的管理变得安全、高效且轻松。无论您是开发者还是系统管理员,它都是您配置开发环境的得力助手。
## 项目结构
## ✨ 功能特点
```
PathEditor/
├── src/
│ ├── main.c # 程序入口,构建 UI 布局
│ ├── callbacks.c # 按钮回调 + 自定义输入对话框
│ ├── registry.c # 注册表读写操作
│ └── utils.c # 编码转换 + 权限检测 + 斑马纹样式
├── include/
│ ├── globals.h # 全局控件句柄与常量
│ ├── callbacks.h
│ ├── registry.h
│ └── utils.h
├── libs/ # IUP 3.31 库文件(预编译)
├── ico/ # 应用图标与资源文件
├── dist/
│ └── installer.iss # Inno Setup 安装包脚本
├── ManagePath.bat # 备用的命令行 PATH 管理脚本
├── CMakeLists.txt # CMake 构建配置
├── Makefile # GNU Make 构建配置(备用)
└── CLAUDE.md # Claude Code 项目指南
```
* **🛡️ 安全第一**
* **自动备份**:每次保存前自动备份注册表,防止意外。
* **只读模式**:非管理员运行时自动切换到只读模式,防止误操作。
* **权限检测**:智能检测当前运行权限。
## 功能特点
* **📑 双视图管理**
* 完美支持 **System (系统变量)****User (用户变量)** 的分离查看与编辑。
* 清晰的 Tab 标签页切换。
- **可视化编辑** — 以列表形式直观查看和管理系统 PATH 变量
- **增删改查** — 新建、编辑、删除条目,支持从文件管理器直接选择目录
- **拖拽排序** — 上移/下移按钮调整路径优先级
- **权限检测** — 非管理员模式自动切换为只读,防止误操作
- **自定义输入框** — 支持超长路径的编辑(80 字符可见宽度)
- **斑马纹列表** — 交替行背景色,方便阅读
- **双击编辑** — 双击列表项直接编辑对应路径
- **即时广播** — 保存后通过 `WM_SETTINGCHANGE` 通知系统
* **🔴 智能诊断与维护**
* **无效路径高亮**:自动检测路径是否存在,不存在的显示为红色。
* **重复路径高亮**:自动检测重复项,重复的显示为橙色。
* **一键清理**:智能移除所有无效和重复的路径,保持环境整洁。
## 工作原理
* **📂 高效交互**
* **拖拽支持**:直接将文件夹拖入窗口即可添加(支持管理员模式下的 UIPI 穿透)。
* **实时搜索**:顶部搜索框支持不区分大小写的实时过滤查找。
* **快捷键**:支持 Delete 键快速删除选中项。
程序直接读写 Windows 注册表键
* **🔄 导入导出**
* **导出备份**:将 PATH 导出为 JSON 文件,方便备份和迁移。
* **导入恢复**:从 JSON 文件导入路径配置。
* **格式兼容**:支持旧版 TXT 格式导入。
```
HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\Path
```
* **便捷管理**
* **新建**:添加新路径到列表。
* 📂 **浏览**:直接从文件资源管理器选择目录添加。
* ✏️ **编辑**:双击或点击按钮修改现有路径。
* 🗑️ **删除**:移除不需要的路径。
* ⬆️⬇️ **排序**:上移/下移调整路径优先级。
PATH 值使用 `REG_EXPAND_SZ` 类型,支持 `%SystemRoot%` 等环境变量展开
* **轻量级**:原生 C 语言编写,无臃肿依赖,运行速度极快
**编码转换**IUP 控件使用 UTF-8Windows 注册表 API 使用 UTF-16`utils.c` 提供 `wide_to_utf8()``utf8_to_wide()` 完成双向转换。
## 🛠️ 架构与二次开发
## 下载与安装
本项目注重代码的模块化和可维护性,采用了经典的 **MVC 分层架构**,非常适合作为 C 语言桌面程序开发的参考:
从 [Releases](https://github.com/LHY0125/PathEditor/releases) 页面下载 `PathEditorSetup.exe` 安装包。
* **分层设计**
* `src/core/` (Model): 核心数据与业务逻辑,完全脱离 UI 框架(无任何 `<iup.h>` 依赖)。
* `src/ui/` (View): 负责界面布局与组件的纯视觉展示。
* `src/controller/` (Controller): 负责连接用户交互与底层数据。
* `src/utils/` (Utils): 纯粹的底层工具类封装(系统级调用、字符串处理)。
* **热配置系统**:所有 UI 参数(窗口大小、按钮文本、布局间距等)均通过 `lua/config.lua` 配置,修改无需重新编译即可生效。
* **清晰的应用状态**:摒弃了脆弱的全局变量模式,采用 `AppContext` 统一管理应用运行时的上下文状态,通过指针传递,安全可靠。
* **开发工具库**
* 统一错误码系统 (`utils/error_code.h`)
* 安全字符串函数 (`utils/safe_string.h`)
* 日志系统 (`utils/logger.h`)
> **注意:** 安装后必须以管理员身份运行,否则只能查看,无法保存更改。
## 📦 下载与安装
## 从源码构建
您可以从 [Releases](https://github.com/LHY0125/PathEditor/releases) 页面下载最新的安装包 (`PathEditorSetup.exe`)。
安装完成后,请**以管理员身份运行**程序以确保能够保存对系统环境变量的修改。
## 🛠️ 构建指南
如果您想从源码构建本项目,请按照以下步骤操作:
### 环境要求
| 工具 | 说明 |
|------|------|
| Windows 操作系统 | 需 Windows API 支持 |
| MinGW-w64 (GCC) | 编译器,路径 `D:\settings\Language\C\mingw64` |
| CMake 3.15+ | 构建工具 |
| IUP 3.31 | GUI 库(已包含在 `libs/` 中) |
* Windows 操作系统
* GCC 编译器 (推荐 MinGW-w64)
* CMake 工具 (推荐使用 CMake 构建)
* IUP 库 (已包含在 `libs` 目录下)
* Inno Setup 6 (仅打包需要)
### 编译
### 编译步骤 (推荐使用 CMake)
```bash
git clone https://github.com/LHY0125/PathEditor.git
cd PathEditor
本项目已迁移至 CMake 构建系统,支持生成更标准的构建文件并集成到各大 IDE。
# 配置(MinGW Makefiles 生成器)
cmake -B build -G "MinGW Makefiles"
1. 克隆仓库:
# 编译
cmake --build build
```
```bash
git clone https://github.com/LHY0125/PathEditor.git
cd PathEditor
```
编译产出在 `bin/PathEditor.exe`
2. 使用 CMake 配置和编译:
**其他 CMake 选项:**
```bash
# 生成构建系统 (以 MinGW 为例)
cmake -B build -G "MinGW Makefiles"
# 编译项目
cmake --build build
```
```bash
# Debug 模式(带调试符号)
cmake -B build -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Debug
cmake --build build
3. 运行:
编译成功后,可执行文件位于 `build/PathEditor.exe`。
# Release 优化
cmake -B build -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release
cmake --build build
```
### 打包 (可选)
### 打包安装程序
本项目使用 Inno Setup 生成安装包。
使用 [Inno Setup 6](https://jrsoftware.org/isdl.php) 生成 `.exe` 安装包:
1. 确保已安装 [Inno Setup 6](https://jrsoftware.org/isdl.php)。
2. 运行根目录下的 `build_installer.bat` 脚本。
3. 生成的安装包将位于 `dist/dist/PathEditorSetup.exe`。
1. 确保已安装 Inno Setup 6
2. 先编译项目生成 `bin/PathEditor.exe` 及所需 DLL
3. 用 Inno Setup 编译 `dist/installer.iss`
4. 安装包输出至 `dist/dist/PathEditorSetup.exe`
## 📝 使用说明
## 使用说明
1. **启动**:右键点击程序图标,选择“以管理员身份运行”。
2. **查看**:程序启动后会自动加载当前的系统 PATH 变量。
* **红色**条目表示路径不存在。
* **橙色**条目表示路径重复。
3. **搜索**:在顶部输入关键词可快速筛选。
4. **修改**
* 拖拽文件夹到列表可直接添加。
* 使用右侧按钮栏进行常规操作。
* 点击“一键清理”可自动删除无效和重复项。
5. **保存**:操作完成后,务必点击底部的【确定】按钮保存更改。
6. **生效**:保存后,某些正在运行的程序可能需要重启才能识别新的环境变量。CMD 或 PowerShell 窗口需要重新打开。
1. **启动** — 右键程序图标,选择「以管理员身份运行」
2. **查看** — 程序启动后自动加载当前系统 PATH 到列表
3. **新建** — 点击「新建」按钮,输入路径后确认,新条目追加到列表末尾
4. **编辑** — 选中条目后点击「编辑」,或直接双击条目
5. **浏览** — 点击「浏览」打开文件夹选择器,选中目录后自动添加
6. **删除** — 选中条目后点击「删除」
7. **排序** — 选中条目后点击「上移」或「下移」调整顺序
8. **保存** — 操作完成后点击「确定」写入注册表并广播变更
9. **生效** — CMD / PowerShell 窗口需要重新打开才能识别新变量;部分程序可能需要重启
## 👤 作者信息
**命令行备选方案:** 项目附带 `ManagePath.bat` 脚本,提供导出、导入和备份 PATH 的功能,无需 GUI 也可操作。
* **作者**LHY
* **邮箱**<3364451258@qq.com>
* **GitHub**[https://github.com/LHY0125/PathEditor](https://github.com/LHY0125/PathEditor)
## 许可证
如果您觉得这个工具对您有帮助,请给我的 GitHub 仓库点个 Star ⭐️
本项目基于 [MIT License](LICENSE) 开源。
## 📄 许可证
Copyright (c) 2026 LHY
本项目基于 MIT 许可证开源,您可以在遵守许可证条款的前提下自由使用、修改和分发本项目的代码。
详细信息请参阅 [LICENSE](LICENSE) 文件。
Copyright © 2026 LHY. All Rights Reserved.
Binary file not shown.
+12
View File
@@ -0,0 +1,12 @@
@echo off
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
+2 -2
View File
@@ -37,8 +37,8 @@ Name: "english"; MessagesFile: "compiler:Default.isl"
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
[Files]
Source: "d:\Code\doing_exercises\programs\PathEditor\bin\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
Source: "d:\Code\doing_exercises\programs\PathEditor\bin\*.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "d:\Code\doing_exercises\programs\PathEditor\build\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
Source: "d:\Code\doing_exercises\programs\PathEditor\build\*.dll"; DestDir: "{app}"; Flags: ignoreversion
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
[Icons]
-20
View File
@@ -1,20 +0,0 @@
#ifndef CALLBACKS_H
#define CALLBACKS_H
#include <iup.h>
// 按钮回调
int btn_new_cb(Ihandle* self);
int btn_edit_cb(Ihandle* self);
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_ok_cb(Ihandle* self);
int btn_cancel_cb(Ihandle* self);
int btn_help_cb(Ihandle* self);
// 双击回调
int list_dblclick_cb(Ihandle* self, int item, char* text);
#endif // CALLBACKS_H
+35
View File
@@ -0,0 +1,35 @@
#ifndef CALLBACKS_H
#define CALLBACKS_H
#include <iup.h>
// 按钮回调
int btn_new_cb(Ihandle *self);
int btn_edit_cb(Ihandle *self);
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_import_cb(Ihandle *self);
int btn_export_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);
// 载入数据与更新UI
void load_all_paths(void);
#endif // CALLBACKS_H
+22
View File
@@ -0,0 +1,22 @@
#ifndef APP_CONTEXT_H
#define APP_CONTEXT_H
#include "utils/string_ext.h"
#include <iup.h>
// 应用上下文结构体,用于存储应用运行时的状态
typedef struct {
StringList sys_paths;
StringList user_paths;
} AppContext;
// 创建应用上下文
AppContext* create_app_context(void);
// 销毁应用上下文
void destroy_app_context(AppContext* ctx);
// 获取应用上下文
AppContext* get_app_context(Ihandle *ih);
#endif // APP_CONTEXT_H
+20
View File
@@ -0,0 +1,20 @@
#ifndef IMPORT_EXPORT_H
#define IMPORT_EXPORT_H
#include "utils/string_ext.h"
#include "utils/error_code.h"
#define EXPORT_VERSION "1.0"
typedef struct {
StringList system;
StringList user;
} ExportData;
// 导出 PATH 到文件
ErrorCode export_paths_to_file(const ExportData *data, const char *filepath);
// 从文件导入 PATH
ErrorCode import_paths_from_file(const char *filepath, ExportData *data);
#endif // IMPORT_EXPORT_H
+34
View File
@@ -0,0 +1,34 @@
#ifndef LUA_CONFIG_H
#define LUA_CONFIG_H
#include <lua.h>
// 初始化 Lua 配置系统
// 返回值: 0 成功, -1 失败
int lua_config_init(void);
// 销毁 Lua 配置系统
void lua_config_destroy(void);
// 获取字符串配置值
// section: 配置章节名 (如 "app", "dialog", "button")
// key: 配置键名 (如 "name", "size", "rastersize")
// 返回值: 配置值字符串, 失败时返回 NULL
const char* lua_config_get_string(const char* section, const char* key);
// 获取整型配置值
// section: 配置章节名
// key: 配置键名
// default_value: 默认值 (当配置不存在或转换失败时返回)
// 返回值: 配置值或默认值
int lua_config_get_int(const char* section, const char* key, int default_value);
// 重新加载配置文件
// 返回值: 0 成功, -1 失败
int lua_config_reload(void);
// 获取配置加载状态
// 返回值: 1 已加载, 0 未加载
int lua_config_is_loaded(void);
#endif // LUA_CONFIG_H
+19
View File
@@ -0,0 +1,19 @@
#ifndef PATH_MANAGER_H
#define PATH_MANAGER_H
#include "utils/string_ext.h"
#include "utils/error_code.h"
// 移除列表中指定索引的项
ErrorCode path_manager_remove_at(StringList *list, int index);
// 上移指定索引的项
ErrorCode path_manager_move_up(StringList *list, int index);
// 下移指定索引的项
ErrorCode path_manager_move_down(StringList *list, int index);
// 清理无效和重复的路径
ErrorCode path_manager_clean(StringList *list);
#endif // PATH_MANAGER_H
+15
View File
@@ -0,0 +1,15 @@
#ifndef REGISTRY_SERVICE_H
#define REGISTRY_SERVICE_H
#include "utils/string_ext.h"
#include "utils/error_code.h"
// 加载系统变量和用户变量到字符串列表
ErrorCode load_system_paths(StringList *list);
ErrorCode load_user_paths(StringList *list);
// 从字符串列表保存系统变量和用户变量
ErrorCode save_system_paths(const StringList *list);
ErrorCode save_user_paths(const StringList *list);
#endif // REGISTRY_SERVICE_H
-24
View File
@@ -1,24 +0,0 @@
#ifndef GLOBALS_H
#define GLOBALS_H
#include <iup.h>
// 注册表路径常量
#define REG_PATH L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment"
#define REG_VALUE L"Path"
// 全局控件句柄声明
extern Ihandle *dlg; // 主对话框句柄
extern Ihandle *list_path; // 路径列表控件句柄
extern Ihandle *lbl_status; // 状态标签句柄
extern Ihandle *btn_new; // 新增按钮句柄
extern Ihandle *btn_edit; // 编辑按钮句柄
extern Ihandle *btn_browse; // 浏览按钮句柄
extern Ihandle *btn_del; // 删除按钮句柄
extern Ihandle *btn_up; // 上移按钮句柄
extern Ihandle *btn_down; // 下移按钮句柄
extern Ihandle *btn_ok; // 确认按钮句柄
extern Ihandle *btn_cancel; // 取消按钮句柄
extern Ihandle *btn_help; // 帮助按钮句柄
#endif // GLOBALS_H
-10
View File
@@ -1,10 +0,0 @@
#ifndef REGISTRY_H
#define REGISTRY_H
// 从注册表加载PATH到列表控件
void load_path();
// 将列表控件中的PATH保存回注册表
void save_path();
#endif // REGISTRY_H
+8
View File
@@ -0,0 +1,8 @@
#ifndef DIALOGS_H
#define DIALOGS_H
// 自定义输入对话框
// 返回值:0-取消,1-确认
int custom_input_dialog(const char *title, const char *label_text, char *buffer, int buffer_size);
#endif // DIALOGS_H
+9
View File
@@ -0,0 +1,9 @@
#ifndef MAIN_WINDOW_H
#define MAIN_WINDOW_H
#include <iup.h>
// 创建主窗口
Ihandle* create_main_window(void);
#endif // MAIN_WINDOW_H
+13
View File
@@ -0,0 +1,13 @@
#ifndef UI_UTILS_H
#define UI_UTILS_H
#include <iup.h>
#include "utils/string_ext.h"
// 刷新单个列表框样式
void refresh_single_list_style(Ihandle *list);
// 同步字符串列表到 UI 列表框
void sync_string_list_to_ui(Ihandle *list_ui, const StringList *str_list);
#endif // UI_UTILS_H
-19
View File
@@ -1,19 +0,0 @@
#ifndef UTILS_H
#define UTILS_H
#include <windows.h>
#include <wchar.h>
// 宽字符转UTF-8
char* wide_to_utf8(const wchar_t* wstr);
// UTF-8转宽字符
wchar_t* utf8_to_wide(const char* str);
// 检查管理员权限
int check_admin();
// 刷新列表样式(斑马纹)
void refresh_list_style();
#endif // UTILS_H
+19
View File
@@ -0,0 +1,19 @@
#ifndef ERROR_CODE_H
#define ERROR_CODE_H
typedef enum {
ERR_OK = 0, // 成功
ERR_FAILED = -1, // 失败
ERR_NULL_PTR = -2, // 空指针
ERR_OUT_OF_MEMORY = -3, // 内存不足
ERR_FILE_NOT_FOUND = -4, // 文件不存在
ERR_PERMISSION_DENIED = -5, // 权限拒绝
ERR_INVALID_FORMAT = -6, // 无效格式
ERR_REGISTRY_FAILED = -7, // 注册表操作失败
ERR_NOT_FOUND = -8, // 未找到
ERR_EXISTS = -9 // 已存在
} ErrorCode;
const char* error_code_to_string(ErrorCode code);
#endif // ERROR_CODE_H
+33
View File
@@ -0,0 +1,33 @@
#ifndef LOGGER_H
#define LOGGER_H
// 日志级别
typedef enum {
LOG_LEVEL_DEBUG, // 调试日志级别
LOG_LEVEL_INFO, // 信息日志级别
LOG_LEVEL_WARN, // 警告日志级别
LOG_LEVEL_ERROR // 错误日志级别
} LogLevel;
// 初始化日志系统
void log_init(const char *log_file, LogLevel level);
// 销毁日志系统
void log_destroy(void);
// 日志函数
void log_debug(const char *fmt, ...);
// 信息日志函数
void log_info(const char *fmt, ...);
// 警告日志函数
void log_warn(const char *fmt, ...);
// 错误日志函数
void log_error(const char *fmt, ...);
// 设置日志级别
void log_set_level(LogLevel level);
#endif // LOGGER_H
+13
View File
@@ -0,0 +1,13 @@
#ifndef OS_ENV_H
#define OS_ENV_H
// 检查是否以管理员权限运行
int check_admin(void);
// 检查路径是否有效
int is_path_valid(const char *path);
// 备份注册表
void backup_registry(void);
#endif // OS_ENV_H
+15
View File
@@ -0,0 +1,15 @@
#ifndef SAFE_STRING_H
#define SAFE_STRING_H
#include <stddef.h>
// 安全字符串操作函数
char* safe_strcpy(char *dst, size_t dst_size, const char *src);
// 安全字符串拼接函数
char* safe_strcat(char *dst, size_t dst_size, const char *src);
// 安全字符串复制函数
char* safe_strdup(const char *src);
#endif // SAFE_STRING_H
+24
View File
@@ -0,0 +1,24 @@
#ifndef STRING_EXT_H
#define STRING_EXT_H
#include <wchar.h>
// 简单字符串列表结构
typedef struct
{
char **items;
int count;
int capacity;
} StringList;
// 字符串列表
void init_string_list(StringList *list);
void add_string_list(StringList *list, const char *str);
void clear_string_list(StringList *list);
// 字符串转换函数
char *wide_to_utf8(const wchar_t *wstr);
wchar_t *utf8_to_wide(const char *str);
char *stristr(const char *haystack, const char *needle);
#endif // STRING_EXT_H
View File
View File
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.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More