从 Clash Verge Rev 2.3.1 定制 CV-Damao:隔离、排错与 Windows 打包记录
本文记录将 Clash Verge Rev 2.3.1 定制为独立 Windows 客户端 CV-Damao 的过程。目标不只是修改名称,而是让定制版能够和官方版本同时安装、同时运行,并解决打包后一直停留在 Loading CV-Damao... 的问题。
最终目标
定制版需要满足以下要求:
- 应用名称为
CV-Damao,程序名为cv-damao.exe。 - 不覆盖官方版本的配置、快捷方式、自启动项和 URL 协议。
- 不停止、卸载或调用官方的 Windows 服务。
- 两套 GUI、数据目录、普通代理内核和控制端口可以并行运行。
- 不使用官方自动更新渠道。
- 能生成可正常安装的 Windows NSIS 安装包。
需要提前说明:Windows 系统代理和 TUN 都是系统级资源,无法由两个客户端同时独立接管。
一、品牌与应用标识隔离
首先修改应用的基础标识:
- 产品名称:
CV-Damao - 可执行文件:
cv-damao.exe - Tauri Identifier:
app.cv-damao.desktop - URL 协议:
cv-damao:// - 单实例端口:从
33331改为33332 - Mihomo 控制端口:从
9097改为19097 - 默认使用随机 mixed port
同时修改了窗口标题、托盘提示、启动加载文字、PE 元数据、便携包名称和安装包名称。
应用数据目录也随 Identifier 隔离到:
%APPDATA%\app.cv-damao.desktop
备份目录、自启动快捷方式和 URL 协议注册项均使用新的名称,避免和官方版本互相覆盖。
侧栏原本使用带有 Clash Verge 字样的 SVG 字标,最后也替换为 CV-Damao 文本,确保主要用户界面不再显示旧品牌。
二、安装器不能操作官方版本
原始 NSIS 脚本包含停止进程、管理服务、删除快捷方式和清理数据的逻辑。对于定制版,这些操作可能误伤已经安装的官方客户端。
因此修改 src-tauri/packages/windows/installer.nsi,移除了可能执行以下操作的代码:
- 停止官方 GUI 或代理内核。
- 停止、卸载官方 Windows 服务。
- 删除官方自启动项。
- 删除官方快捷方式或数据目录。
安装和卸载 CV-Damao 时,只处理自己的文件和注册项。
三、Windows 服务隔离
这是整个定制过程中最需要注意的部分。
上游预编译的 clash-verge-service.exe 将以下内容硬编码在二进制中:
服务名:clash_verge_service
IPC:\\.\pipe\clash-verge-service
仅修改 Rust 客户端里的名称不能得到第二套独立服务,反而可能让定制版连接并管理官方服务。实测中,官方服务还会代替定制版启动仓库内的 Mihomo,导致 GUI 退出后进程仍然存在,并占用 release 目录中的文件。
最终方案是:Windows 定制版完全禁用共享服务模式,固定使用自身 sidecar。
在 src-tauri/src/core/core.rs 中,Windows 下的初始化和重启都直接调用:
start_core_by_sidecar()
在 src-tauri/src/cmd/service.rs 中:
- 安装、卸载、重装和修复服务的命令直接返回禁用提示。
- 服务可用性检测固定返回
false。
这样定制版不会探测、调用或管理官方的 clash_verge_service。
代价是:sidecar 在普通用户权限下运行,需要管理员权限的 TUN 功能可能不可用。要实现真正独立的第二套 Windows 服务,需要重新构建服务程序,并修改其服务名和 IPC,而不是只修改 GUI 仓库。
四、Loading 卡死的定位过程
最棘手的问题是:release 程序能够启动,后端配置验证和 Mihomo 也正常,但界面一直停留在:
Loading CV-Damao...
后端日志显示窗口已经创建,但前端始终没有调用 update_ui_stage,因此初始化遮罩没有被移除。这说明问题不在 Mihomo 或 Rust 初始化,而在 React 启动阶段。
为了捕获 release WebView 的错误,临时在窗口创建代码中加入:
window.open_devtools();
DevTools 控制台显示真正的异常:
Uncaught TypeError: Cannot set properties of undefined (setting 'AsyncMode')
继续检查构建产物后发现,Vite 的自定义 manualChunks 生成了两个互相导入的块:
react-*.js
small-vendors-*.js
这造成循环依赖和模块初始化顺序错误,React 还未完成初始化就被其他 vendor 代码使用,最终导致整个前端入口崩溃。
修复方法很简单:删除 vite.config.mts 中自定义的 vendor manualChunks,让 Rollup 自动分块。
重新构建后:
- 循环导入消失。
- React 正常挂载。
initial-loading-overlay被移除。- 日志依次出现
Loading、DomReady、ResourcesLoaded和“UI已完全加载”。
确认问题解决后,临时加入的 open_devtools() 也被删除。
五、自动更新处理
定制版没有上游项目的 updater 私钥,也不应该下载官方更新包。因此进行了以下调整:
- 默认关闭自动更新。
- 更新地址改为无效的保留域名。
createUpdaterArtifacts设置为false。
否则 Tauri 可能在构建阶段要求更新签名私钥,运行后也可能错误接收官方版本的更新。
六、构建过程中的 Windows 缓存问题
主要构建命令为:
pnpm install --frozen-lockfile --force
pnpm prebuild
pnpm web:build
pnpm exec tauri build --bundles nsis --ci
Rust release 编译成功后,NSIS 阶段曾多次报错:
对象管理器在检索对象时遇到重分析点。 (os error 4395)
问题来自 Tauri 的 NSIS 工具缓存,而不是项目代码。缓存位置为:
%LOCALAPPDATA%\tauri\NSIS
处理方法是将损坏缓存移动到备份目录,让 Tauri 重新下载和解压 NSIS:
Move-Item "$env:LOCALAPPDATA\tauri\NSIS" \
"$env:LOCALAPPDATA\tauri\NSIS.stale-$(Get-Date -Format yyyyMMddHHmmss)"
如果 release 主程序已经编译完成,不必重新编译 Rust,可以直接重新打包:
pnpm exec tauri bundle --bundles nsis --ci
这能明显节省重试时间。
另一个构建问题是 verge-mihomo.exe 被运行中的定制版占用。清理时必须根据可执行文件完整路径筛选,只结束仓库目录下的进程,不能按进程名直接结束,以免影响官方版本。
七、验证方式
最终不能只看“编译成功”,还需要实际启动 release 程序验证。
检查内容包括:
- 主窗口不再停留在 Loading。
- 首页和设置页可以正常渲染。
- 日志出现完整的 UI 加载阶段。
verge-mihomo.exe的父进程是cv-damao.exe。- 官方
clash-verge-service.exe的启动时间和进程状态没有被定制版改变。 - 测试结束时只关闭仓库路径下的定制版进程。
- PE 产品名称和文件描述均为
CV-Damao。
最终运行关系如下:
cv-damao.exe
└── verge-mihomo.exe
clash-verge-service.exe # 官方服务,独立存在,未被调用
格式和构建检查也全部通过:
pnpm prettier --check src/pages/_layout.tsx vite.config.mts
cargo fmt --manifest-path src-tauri/Cargo.toml --check
git diff --check
pnpm web:build
Rust 编译仍有上游已有的生命周期提示,但不影响构建和运行。
八、最终产物
安装文件:
src-tauri\target\release\bundle\nsis\CV-Damao_2.3.1_x64-setup.exe
最终信息:
大小:47,984,833 字节
SHA-256:e475a578e8126ee8ee0b866c3d423f4b98cf61d79e58bb8441f4cf394c9c1bc8
产品名称:CV-Damao
文件描述:CV-Damao
主程序:
src-tauri\target\release\cv-damao.exe
SHA-256:d9d1157007dcd22ac358e54a6ecabf75f9c7bffaeac74fbbb8832cc08fc8cbe6
安装时应运行 CV-Damao_2.3.1_x64-setup.exe,而不是直接运行构建目录中的主程序。
当前安装包没有 Authenticode 签名,因此 Windows 可能显示“未知发布者”或 SmartScreen 提示。
总结
这次定制的重点并不是换图标和名称,而是处理三类真正影响可用性的隔离问题:
- 身份隔离:应用 ID、数据目录、协议、端口、快捷方式和自启动项全部独立。
- 运行隔离:Windows 下固定使用自身 sidecar,不连接或管理官方服务。
- 构建修复:移除导致 React 初始化异常的
manualChunks,解决 release 界面 Loading 卡死。
最终 CV-Damao 可以与官方客户端同时安装和运行,两套普通代理内核互不干扰。仍需注意系统代理和 TUN 属于 Windows 全局资源,使用时应由用户明确选择其中一个客户端接管。