mirror of
https://github.com/LHY0125/PathEditor.git
synced 2026-05-10 02:09:46 +08:00
feat: 添加 Windows 环境变量编辑器 GUI 应用程序
- 基于 IUP GUI 库开发 Windows 环境变量编辑器 - 实现 PATH 变量的加载、编辑、保存功能 - 提供新建、编辑、浏览、删除、上下移动路径等操作 - 添加管理员权限检查,确保系统变量修改权限 - 包含完整的项目构建配置(Makefile)和依赖库
This commit is contained in:
+160
@@ -0,0 +1,160 @@
|
||||
#include "callbacks.h"
|
||||
#include "globals.h"
|
||||
#include "registry.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// 按钮回调:新建
|
||||
int btn_new_cb(Ihandle *self)
|
||||
{
|
||||
char buffer[1024] = "";
|
||||
if (IupGetParam("新建环境变量", NULL, NULL, "路径: %s\n", buffer, NULL))
|
||||
{
|
||||
if (strlen(buffer) > 0)
|
||||
{
|
||||
int count = IupGetInt(list_path, "COUNT");
|
||||
count++;
|
||||
IupSetAttributeId(list_path, "", count, buffer);
|
||||
IupSetInt(list_path, "COUNT", count);
|
||||
IupSetInt(list_path, "VALUE", count);
|
||||
}
|
||||
}
|
||||
return IUP_DEFAULT;
|
||||
}
|
||||
|
||||
// 按钮回调:编辑
|
||||
int btn_edit_cb(Ihandle *self)
|
||||
{
|
||||
int selected = IupGetInt(list_path, "VALUE");
|
||||
if (selected == 0)
|
||||
return IUP_DEFAULT;
|
||||
|
||||
char *current_val = IupGetAttributeId(list_path, "", selected);
|
||||
char buffer[4096]; // 假设单个路径不超过4096
|
||||
strncpy(buffer, current_val, 4096);
|
||||
buffer[4095] = '\0';
|
||||
|
||||
if (IupGetParam("编辑环境变量", NULL, NULL, "路径: %s\n", buffer, NULL))
|
||||
{
|
||||
if (strlen(buffer) > 0)
|
||||
{
|
||||
IupSetAttributeId(list_path, "", selected, buffer);
|
||||
}
|
||||
}
|
||||
return IUP_DEFAULT;
|
||||
}
|
||||
|
||||
// 按钮回调:浏览
|
||||
int btn_browse_cb(Ihandle *self)
|
||||
{
|
||||
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)
|
||||
{
|
||||
int count = IupGetInt(list_path, "COUNT");
|
||||
count++;
|
||||
IupSetAttributeId(list_path, "", count, value);
|
||||
IupSetInt(list_path, "COUNT", count);
|
||||
IupSetInt(list_path, "VALUE", count);
|
||||
}
|
||||
}
|
||||
IupDestroy(filedlg);
|
||||
return IUP_DEFAULT;
|
||||
}
|
||||
|
||||
// 按钮回调:删除
|
||||
int btn_del_cb(Ihandle *self)
|
||||
{
|
||||
int selected = IupGetInt(list_path, "VALUE");
|
||||
if (selected == 0)
|
||||
return IUP_DEFAULT;
|
||||
|
||||
IupSetAttribute(list_path, "REMOVEITEM", "SELECTED");
|
||||
return IUP_DEFAULT;
|
||||
}
|
||||
|
||||
// 按钮回调:上移
|
||||
int btn_up_cb(Ihandle *self)
|
||||
{
|
||||
int selected = IupGetInt(list_path, "VALUE");
|
||||
if (selected <= 1)
|
||||
return IUP_DEFAULT; // 已经是第一个或未选中
|
||||
|
||||
char *current = IupGetAttributeId(list_path, "", selected);
|
||||
char *prev = IupGetAttributeId(list_path, "", selected - 1);
|
||||
|
||||
// 交换内容
|
||||
char buf_curr[4096], buf_prev[4096];
|
||||
strncpy(buf_curr, current, 4096);
|
||||
buf_curr[4095] = '\0';
|
||||
strncpy(buf_prev, prev, 4096);
|
||||
buf_prev[4095] = '\0';
|
||||
|
||||
IupSetAttributeId(list_path, "", selected, buf_prev);
|
||||
IupSetAttributeId(list_path, "", selected - 1, buf_curr);
|
||||
|
||||
IupSetInt(list_path, "VALUE", selected - 1);
|
||||
return IUP_DEFAULT;
|
||||
}
|
||||
|
||||
// 按钮回调:下移
|
||||
int btn_down_cb(Ihandle *self)
|
||||
{
|
||||
int selected = IupGetInt(list_path, "VALUE");
|
||||
int count = IupGetInt(list_path, "COUNT");
|
||||
if (selected == 0 || selected >= count)
|
||||
return IUP_DEFAULT;
|
||||
|
||||
char *current = IupGetAttributeId(list_path, "", selected);
|
||||
char *next = IupGetAttributeId(list_path, "", selected + 1);
|
||||
|
||||
char buf_curr[4096], buf_next[4096];
|
||||
strncpy(buf_curr, current, 4096);
|
||||
buf_curr[4095] = '\0';
|
||||
strncpy(buf_next, next, 4096);
|
||||
buf_next[4095] = '\0';
|
||||
|
||||
IupSetAttributeId(list_path, "", selected, buf_next);
|
||||
IupSetAttributeId(list_path, "", selected + 1, buf_curr);
|
||||
|
||||
IupSetInt(list_path, "VALUE", selected + 1);
|
||||
return IUP_DEFAULT;
|
||||
}
|
||||
|
||||
// 按钮回调:确定
|
||||
int btn_ok_cb(Ihandle *self)
|
||||
{
|
||||
save_path();
|
||||
return IUP_DEFAULT;
|
||||
}
|
||||
|
||||
// 按钮回调:取消
|
||||
int btn_cancel_cb(Ihandle *self)
|
||||
{
|
||||
IupExitLoop();
|
||||
return IUP_DEFAULT;
|
||||
}
|
||||
|
||||
// 按钮回调:帮助
|
||||
int btn_help_cb(Ihandle *self)
|
||||
{
|
||||
IupMessage("使用说明",
|
||||
"1. 本程序用于编辑系统环境变量 PATH。\n"
|
||||
"2. 必须以【管理员身份】运行才能保存更改。\n"
|
||||
"3. 操作说明:\n"
|
||||
" - 新建:添加新路径到列表末尾。\n"
|
||||
" - 编辑:修改选中的路径。\n"
|
||||
" - 浏览:从文件系统选择目录添加。\n"
|
||||
" - 删除:移除选中的路径。\n"
|
||||
" - 上移/下移:调整路径优先级。\n"
|
||||
"4. 点击【确定】保存更改并生效。\n"
|
||||
"5. 注意:某些正在运行的程序可能需要重启才能识别新的环境变量。");
|
||||
return IUP_DEFAULT;
|
||||
}
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
#include <windows.h>
|
||||
#include <iup.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <wchar.h>
|
||||
#include "globals.h"
|
||||
#include "utils.h"
|
||||
#include "registry.h"
|
||||
#include "callbacks.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <direct.h>
|
||||
#endif
|
||||
|
||||
// 全局控件定义
|
||||
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;
|
||||
|
||||
// 主函数
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// 设置控制台编码为UTF-8,防止中文乱码
|
||||
#ifdef _WIN32
|
||||
system("chcp 65001 > nul"); // 设置控制台编码为UTF-8
|
||||
SetConsoleOutputCP(65001); // 设置控制台输出编码
|
||||
SetConsoleCP(65001); // 设置控制台输入编码
|
||||
_mkdir("records");
|
||||
#endif
|
||||
|
||||
// 强制设置 UTF8MODE 环境变量,必须在 IupOpen 之前
|
||||
putenv("IUP_UTF8MODE=YES");
|
||||
|
||||
IupOpen(&argc, &argv);
|
||||
IupSetGlobal("UTF8MODE", "YES");
|
||||
|
||||
// 创建列表控件
|
||||
list_path = IupList(NULL);
|
||||
IupSetAttribute(list_path, "EXPAND", "YES");
|
||||
IupSetAttribute(list_path, "VISIBLELINES", "15");
|
||||
IupSetAttribute(list_path, "MULTIPLE", "NO");
|
||||
|
||||
// 创建右侧按钮
|
||||
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);
|
||||
|
||||
// 设置按钮回调
|
||||
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);
|
||||
|
||||
// 设置按钮大小 (宽度)
|
||||
IupSetAttribute(btn_new, "RASTERSIZE", "80x");
|
||||
IupSetAttribute(btn_edit, "RASTERSIZE", "80x");
|
||||
IupSetAttribute(btn_browse, "RASTERSIZE", "80x");
|
||||
IupSetAttribute(btn_del, "RASTERSIZE", "80x");
|
||||
IupSetAttribute(btn_up, "RASTERSIZE", "80x");
|
||||
IupSetAttribute(btn_down, "RASTERSIZE", "80x");
|
||||
|
||||
Ihandle *vbox_btns = IupVbox(
|
||||
btn_new, btn_edit, btn_browse, btn_del,
|
||||
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);
|
||||
IupSetAttribute(hbox_main, "GAP", "10");
|
||||
IupSetAttribute(hbox_main, "MARGIN", "10x10");
|
||||
|
||||
// 状态标签
|
||||
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", "80x25");
|
||||
IupSetAttribute(btn_cancel, "RASTERSIZE", "80x25");
|
||||
IupSetAttribute(btn_help, "RASTERSIZE", "80x25");
|
||||
|
||||
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 *vbox_all = IupVbox(
|
||||
IupLabel("系统变量 Path:"),
|
||||
hbox_main,
|
||||
hbox_bottom,
|
||||
NULL);
|
||||
IupSetAttribute(vbox_all, "MARGIN", "10x10");
|
||||
IupSetAttribute(vbox_all, "GAP", "5");
|
||||
|
||||
// 创建对话框
|
||||
dlg = IupDialog(vbox_all);
|
||||
IupSetAttribute(dlg, "TITLE", "编辑环境变量 (IUP版)");
|
||||
IupSetAttribute(dlg, "SIZE", "450x350");
|
||||
IupSetAttribute(dlg, "MINBOX", "NO");
|
||||
IupSetAttribute(dlg, "MAXBOX", "NO");
|
||||
|
||||
// 加载数据
|
||||
if (!check_admin())
|
||||
{
|
||||
IupMessage("警告", "程序未以管理员身份运行,您只能查看,无法保存更改!");
|
||||
IupSetAttribute(dlg, "TITLE", "编辑环境变量 (只读模式)");
|
||||
IupSetAttribute(lbl_status, "TITLE", "状态: 只读模式 (权限不足)");
|
||||
}
|
||||
|
||||
IupShowXY(dlg, IUP_CENTER, IUP_CENTER);
|
||||
|
||||
// IUP List APPEND 属性需要在控件 Map 之后才能生效
|
||||
// IupShowXY 会触发 Map
|
||||
load_path();
|
||||
|
||||
IupMainLoop();
|
||||
IupClose();
|
||||
return 0;
|
||||
}
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
#include "registry.h"
|
||||
#include "globals.h"
|
||||
#include "utils.h"
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <wchar.h>
|
||||
|
||||
// 从注册表加载PATH
|
||||
void load_path()
|
||||
{
|
||||
HKEY hKey;
|
||||
LONG res = RegOpenKeyExW(HKEY_LOCAL_MACHINE, REG_PATH, 0, KEY_READ, &hKey);
|
||||
if (res != ERROR_SUCCESS)
|
||||
{
|
||||
char msg[512];
|
||||
snprintf(msg, sizeof(msg), "无法打开注册表键 (HKLM)。\n路径: %ls\n错误码: %ld\n\n请尝试右键点击程序 -> '以管理员身份运行'。", REG_PATH, res);
|
||||
IupMessage("错误", msg);
|
||||
IupSetAttribute(lbl_status, "TITLE", "状态: 注册表读取失败");
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD type, size;
|
||||
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)
|
||||
{
|
||||
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 变量为空!");
|
||||
}
|
||||
|
||||
while (*current)
|
||||
{
|
||||
// 查找下一个分号
|
||||
next_semicolon = wcschr(current, L';');
|
||||
if (next_semicolon)
|
||||
{
|
||||
*next_semicolon = L'\0'; // 临时截断
|
||||
}
|
||||
|
||||
// 跳过空项
|
||||
if (wcslen(current) > 0)
|
||||
{
|
||||
char *utf8_str = wide_to_utf8(current);
|
||||
|
||||
count++;
|
||||
IupSetAttributeId(list_path, "", count, utf8_str);
|
||||
|
||||
free(utf8_str);
|
||||
}
|
||||
|
||||
if (next_semicolon)
|
||||
{
|
||||
current = next_semicolon + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
IupSetInt(list_path, "COUNT", count); // 显式设置列表项数量
|
||||
IupSetInt(list_path, "VALUE", 1); // 选中第一项
|
||||
|
||||
char status_msg[100];
|
||||
sprintf(status_msg, "状态: 已加载 %d 个条目", count);
|
||||
IupSetAttribute(lbl_status, "TITLE", status_msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
IupMessage("错误", "读取 PATH 值内容失败!");
|
||||
IupSetAttribute(lbl_status, "TITLE", "状态: 读取值内容失败");
|
||||
}
|
||||
free(buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
char msg[256];
|
||||
sprintf(msg, "查询 PATH 值大小失败。错误码: %ld", res);
|
||||
IupMessage("错误", msg);
|
||||
IupSetAttribute(lbl_status, "TITLE", "状态: 查询值失败");
|
||||
}
|
||||
RegCloseKey(hKey);
|
||||
}
|
||||
|
||||
// 保存PATH到注册表
|
||||
void save_path()
|
||||
{
|
||||
if (!check_admin())
|
||||
{
|
||||
IupMessage("错误", "需要管理员权限才能保存更改!\n请重新以管理员身份运行程序。");
|
||||
return;
|
||||
}
|
||||
|
||||
int count = IupGetInt(list_path, "COUNT");
|
||||
if (count == 0)
|
||||
{
|
||||
// 警告:清空PATH是很危险的
|
||||
if (IupAlarm("警告", "PATH 为空!这可能导致系统命令无法使用。\n确定要保存吗?", "确定", "取消", NULL) != 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 计算所需缓冲区大小
|
||||
size_t total_len = 0;
|
||||
for (int i = 1; i <= count; i++)
|
||||
{
|
||||
char *item = IupGetAttributeId(list_path, "", i);
|
||||
if (item)
|
||||
{
|
||||
wchar_t *witem = utf8_to_wide(item);
|
||||
total_len += wcslen(witem) + 1; // +1 for ';'
|
||||
free(witem);
|
||||
}
|
||||
}
|
||||
total_len += 1; // null terminator
|
||||
|
||||
wchar_t *buffer = (wchar_t *)malloc(total_len * sizeof(wchar_t));
|
||||
if (!buffer)
|
||||
{
|
||||
IupMessage("错误", "内存分配失败 (保存时)!");
|
||||
return;
|
||||
}
|
||||
buffer[0] = L'\0';
|
||||
|
||||
for (int i = 1; i <= count; i++)
|
||||
{
|
||||
char *item = IupGetAttributeId(list_path, "", 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)
|
||||
{
|
||||
// 使用 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", "状态: 保存失败");
|
||||
}
|
||||
RegCloseKey(hKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
IupMessage("错误", "无法打开注册表进行写入。请检查权限!");
|
||||
IupSetAttribute(lbl_status, "TITLE", "状态: 打开注册表失败");
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
#include "utils.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <iup.h>
|
||||
|
||||
// 宽字符转UTF-8 (用于IUP显示)
|
||||
char *wide_to_utf8(const wchar_t *wstr)
|
||||
{
|
||||
if (!wstr)
|
||||
return NULL;
|
||||
int size_needed = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
|
||||
char *str = (char *)malloc(size_needed);
|
||||
WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, size_needed, NULL, NULL);
|
||||
return str;
|
||||
}
|
||||
|
||||
// UTF-8转宽字符 (用于Windows API)
|
||||
wchar_t *utf8_to_wide(const char *str)
|
||||
{
|
||||
if (!str)
|
||||
return NULL;
|
||||
int size_needed = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
|
||||
wchar_t *wstr = (wchar_t *)malloc(size_needed * sizeof(wchar_t));
|
||||
MultiByteToWideChar(CP_UTF8, 0, str, -1, wstr, size_needed);
|
||||
return wstr;
|
||||
}
|
||||
|
||||
// 检查管理员权限
|
||||
int check_admin()
|
||||
{
|
||||
HKEY hKey;
|
||||
LONG result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", 0, KEY_WRITE, &hKey);
|
||||
if (result == ERROR_SUCCESS)
|
||||
{
|
||||
RegCloseKey(hKey);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user