Windows命令行程序运行监控系统托盘管理工具
- json配置文件
- 系统托盘
- 支持以管理员运行
- 显示隐藏命令行界面,方便查看日志 启动禁用管理
- 可以配置任意多数量的(几十个应该没啥问题)后台命令行
- 当CommandTrayHost退出时,由操作系统保证清理所有的子进程。
- 自定义托盘图标和命令行图标
- 本地化支持
- 自定义菜单层级最多支持40级
- 多实例运行与开机启动支持
- 热键支持
- Crontab计划任务
使用前可以先看一眼wiki
配置文件名必须是config.json
,必须放到CommandTrayHost.exe所在目录。运行一次,会自动生成一个基本的config.json
模板(包括详细文档)。支持的编码为UTF8 UTF-16LE UTF-16BE UTF-32等,支持BOM识别。也就是支持记事本保存的Unicode和UTF-8格式。
配置样例
{
"configs": [
{
// 下面8个一个不能少
"name": "kcptun 1080 8.8.8.1:12345", // 系统托盘菜单名字
"path": "E:\\program\\kcptun-windows-amd64", // cmd的exe所在目录,相对路径是可以的,参考目录是CommandTrayHost.exe所在目录
"cmd": "client_windows_amd64.exe -c client.json", // cmd命令,必须含有.exe
"working_directory": "", // 命令行的工作目录,比如这里的client.json,如果是相对路径,>开头意味着相对于CommandTrayHost.exe,否则相对于path。
"addition_env_path": "", // dll搜索目录,暂时没用到
"use_builtin_console": false, // 是否用CREATE_NEW_CONSOLE,暂时没用到
"is_gui": false, // 是否是 GUI图形界面程序,暂时没用到
"enabled": true, // 是否当CommandTrayHost启动时,自动开始运行
// 下面的是可选参数
"require_admin": false, // 是否要用管理员运行,当CommandTrayHost不是以管理员运行的情况下,显示/隐藏会失效,其他功能正常。
"start_show": false, // 是否以显示(而不是隐藏)的方式启动子程序
"ignore_all": false, // 是否忽略全部启用禁用操作。当为true时,全部启用菜单对本程序无效
"position": [ // 显示窗口的初始位置
0.2, // STARTUPINFO.dwX 大于1就是数值,0-1之间的数值代表相对屏幕分辨率的百分比
200 // STARTUPINFO.dwY, 同上
],
"size": [ // 显示窗口的初始大小
0.5, // STARTUPINFO.dwXSize, 同上
0.5 // STARTUPINFO.dwYSize, 同上
],
"icon": "", // 命令行窗口的图标
"alpha": 170, // 命令行窗口的透明度,0-255之间的整数,0为完全看不见,255完全不透明
"topmost": false, // 命令行窗口置顶
// 可以使用的有alt win shift ctrl 0-9 A-Z 空格或者+号分割
// 或者使用这种形式 "ALT+WIN+CTRL+0x20" 鼠标手柄的键盘码参考
// https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
"hotkey": { // 下面并不需要都出现,可以只设置部分
"disable_enable": "Shift+Win+D", // 启用/禁用切换
"hide_show": "Shift+Win+H", // 显示/隐藏切换
"restart": "Shift+Win+R", // 重启程序
"elevate": "Shift+Win+A", // 以管理员运行本程序
},
"not_host_by_commandtrayhost": false, // 如果设置成了true,那么CommandTrayHost就不会监控它的运行了
"not_monitor_by_commandtrayhost": false, // 如果设置成true同上,但是会随着CommandTrayHost退出而关闭。
"kill_timeout": 200, // 执行关闭操作时,先尝试通知程序自己关闭然后等多少ms,然后再杀进程,默认是200ms
"exclusion_id": 1, // 互斥id,要求是大于0的整数。相同的互斥id启动时,会先杀掉其他
"kill_process_tree": false, // 杀进程的时候同时杀掉其子进程,用于nginx. 为true时,kill_timeout无效
},
{
"name": "kcptun 1081 8.8.8.1:12346",
"path": "E:\\program\\kcptun-windows-amd64",
"cmd": "client_windows_amd64.exe -c client.json",
"working_directory": "E:\\program\\kcptun-windows-amd64\\config2",
"addition_env_path": "",
"use_builtin_console": false,
"is_gui": false,
"enabled": true
// 可选
"crontab_config": { // crontab配置
"crontab": "8 */2 15-16 29 2 *", // crontab语法具体参考Linux
"method": "start", // 支持的有 start restart stop start_count_stop restart_count_stop,最后两个表示count次数的最后一个会执行stop
"count": 0, // 0 表示不只限制,大于0的整数,表示运行多少次就不运行了
// 可选
"enabled": true,
"log": "commandtrayhost.log", // 日志文件名,注释掉本行就禁掉log了
"log_level": 0, // log级别,缺省默认为0。0为仅仅记录crontab触发记录,1附加启动时的信息,2附加下次触发的信息
"start_show": false, // 注释掉的话,使用cache值(如果有),cache禁用的状态下的默认值是false
},
"exclusion_id": 1,
},
{
"name": "herokuapp",
"path": "C:\\Program Files\\nodejs",
"cmd": "node.exe local.js -s yousecret-id.herokuapp.com -l 1090 -m camellia-256-cfb -k ItsATopSecret -r 80",
"working_directory": "E:\\program\\shadowsocks-heroku.git", // 用了一个不同的工作目录
"addition_env_path": "",
"use_builtin_console": false,
"is_gui": false,
"enabled": true
},
{
"name": "shadowsocks",
"path": "E:\\program\\shadowsocks",
"cmd": "Shadowsocks.exe",
"working_directory": "",
"addition_env_path": "",
"use_builtin_console": false,
"is_gui": true,
"enabled": false
},
{
"name": "cow",
"path": "E:\\program\\cow",
"cmd": "cow.exe",
"working_directory": "",
"addition_env_path": "",
"use_builtin_console": false,
"is_gui": false,
"enabled": true
},
{
"name": "aria2",
"path": "E:\\program\\aria2-win-64bit",
"cmd": "aria2c.exe --conf=aria2.conf",
"working_directory": "",
"addition_env_path": "",
"use_builtin_console": false,
"is_gui": false,
"enabled": true,
"kill_timeout": 2000, // 最多等2秒钟,让aria2有足够的时间保存session, 根据硬盘速度可适当增加,比如移动硬盘最好4秒
},
{
"name": "JeliLicenseServer",
"path": "E:\\program\\JeliLicenseServer",
"cmd": "JeliLicenseServer_windows_amd64.exe -u admin -l 127.0.0.153",
"working_directory": "",
"addition_env_path": "",
"use_builtin_console": false,
"is_gui": false,
"enabled": true
},
],
"global": true,
// 可选参数
"require_admin": false, // 是否CommandTrayHost要对自身提权
// 绝大部分情况不需要admin的,但是如果真的需要,自动启动应该会有问题,可以参考使用 https://stefansundin.github.io/elevatedstartup/
"icon": "E:\\icons\\Mahm0udwally-All-Flat-Computer.ico", // 自定义托盘图标路径,空为默认内置 256x256
"icon_size": 256, // 256 32 16
"cmd_menu_max_length": 0, // cmd和path最大字符串个数,0表示不限制,大于0整数表示超过之后用...标出
"lang": "auto", // zh-CN en-US https://msdn.microsoft.com/en-us/library/cc233982.aspx
"groups": [ // groups的值是一个数组,可以有两种类型,一种为数值,一种为object。object代表下级菜单。object必须有name字段
{
"name": "kcptun", // 分级菜单的名字
"groups": [
0, // 编号,是configs的编号。数组下标,从0开始
1,
],
},
{
"name": "shadowsocks",
"groups": [
3, // 顺序就是菜单顺序
2,
4,
],
},
5,
6,
{
"name": "empty test", // 可以没有groups,但是不能没有name
},
],
"enable_groups": true, // 启用分组菜单
"groups_menu_symbol": "+", // 分组菜单标志
"left_click": [
0,
1
], // 左键单击显示/隐藏程序 configs序号,从0开始. 空数组或者注释掉,则显示CommandTrayHost本体
"enable_cache": true, // 启用cache
"conform_cache_expire": true, // CommandTrayHost是否检查cache文件和配置文件,设为false时热加载被禁用
"disable_cache_position": false, // 禁止缓存窗口位置
"disable_cache_size": false, // 禁止缓存窗口大小
"disable_cache_enabled": true, // 禁止缓存启用禁用状态
"disable_cache_show": false, // 禁止缓存显示隐藏状态
"disable_cache_alpha": false, // 禁止缓存透明度 (缓存时只对有alpha值的configs有作用)
// 可以使用的有alt win shift ctrl 0-9 A-Z 空格或者+号分割
// 或者使用这种形式 "ALT+WIN+CTRL+0x20" 鼠标手柄的键盘码参考
// https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
"hotkey": {
"disable_all": "Alt+Win+Shift+D",
"enable_all": "Alt Win + Shift +E",
"hide_all": "Alt+WIN+Shift+H",
"show_all": "AlT Win Shift s",
"restart_all": "ALT+Win+Shift+U",
"elevate": "Alt+wIn+Shift+a",
"exit": "Alt+Win+Shift+X",
"left_click": "Alt+Win+Shift+L",
"right_click": "Alt+Win+Shift+R",
// 下面的五个快捷键,可以对外部程序生效
"add_alpha": "Alt+Ctrl+Win+0x26", // 修改当前激活的任何窗口(要可能)透明度,不仅仅只对本程序托管的有效,其他程序也行
"minus_alpha": "Alt+Ctrl+Win+0x28", //上面上箭头 这里下箭头 Alt+Ctrl+Win+↑↓
"topmost": "Alt+Ctrl+Win+T", // 切换当前窗口的置顶状态
"hide_current": "Alt+Ctrl+Win+H", // 隐藏当前窗口,可以在托盘图标上找到对应项目
"show_all_docked": "Alt+Ctrl+Win+S", // 显示所有被上面这个快捷键隐藏的窗口
},
"repeat_mod_hotkey": false, // 是否长按算多次,Windows XP下面无效
"global_hotkey_alpha_step": 5, // 上面透明度调节的幅度
"show_hotkey_in_menu": true, // 在菜单后面加上成功注册的热键
"enable_hotkey": true,
"start_show_silent": true, // 启动的时候屏幕不会闪(也就是等到获取到窗口才显示)
"auto_hot_reloading_config": false, // 这个为true时,相当于自动点击加载配置弹窗的否
"auto_update": true,
"skip_prerelease": true,
"keep_update_history": false, // 是否保留自动更新时的临时文件
}
提示1: "cmd"
必须包含.exe
.如果要运行批处理.bat, 可以使用 cmd.exe /c
或者cmd.exe /k
.
提示2: 管理员比较复杂,如果不是真的需要。配置中不要出现任何require_admin
。
简而言之:
- 如果CommandTrayHost是以管理员运行的,那么启动的要求特权的子进程没啥问题,但是CommandTrayHost开机启动会比较麻烦,不能用菜单的那个。
- 如果CommandTrayHost是以普通用户运行的,而且没有要求提权,但是 尝试启动了一个要求提权的程序 或者 对程序加上了
"require_admin":true,
, 那么运行时会弹出UAC,授权后是可以正常运行以及重启应用,但是启动后,非特权的CommandTrayHost是没法唤出显示的。
提示3: icon制作可以参考 这里 ,或者从iconarchive下载。
注意: 所有的路径,必须是\\
分割的,这是因为json规定字符串,会自动转义\
之后的字符。
- VS2015 Update3 或者 VS2017 (其实这是vcpkg的要求)
- 安装 vcpkg
- 为当前用户集成vcpkg,以管理员命令行运行(只要用管理员运行一次,以后就不需要管理员权限了)
vcpkg integrate install
- 安装 rapidjson 和 nlohmann::json.
vcpkg install rapidjson rapidjson:x64-windows nlohmann-json nlohmann-json:x64-windows
- 打开
CommandTrayHost.sln
, 点击编译.
为了保证resource.h
和CommandTrayHost.rc
编码为UTF-16LE(UCS-2)带BOM,在git clone
之前,可能需要在%USERPROFILE%\.gitconfig
文件最后面(不存在新建一个)加上如下内容:
[filter "utf16"]
clean = iconv -f utf-16le -t utf-8
smudge = iconv -f utf-8 -t utf-16le
required
看这个文件 CommandTrayHost/CommandTrayHost/language_data.h
-
现在一旦重启某个应用,那么之前的窗口就会被关掉,然后重新开启一个。这样之前的日志就丢失了。希望对每个应用,启动一个独立辅助Console,即使重新启动应用,历史日志(标准IO输出)依然可以保留。
use_builtin_console
就是用来做这个用途的。可以参考的有 ConEmu,看上去必须要注入子进程,将其标准IO导入到ConsoleHelper才行。 -
尝试集成 Elevated Startup
-
UIPI (User Interface Privilege Isolation) Bypass.
ChangeWindowMessageFilterEx