Skip to content

工坊

背景:设置页只认识 Blender 和 MMD 两个名字——用户的其他工具无处停靠。 过程:软件管理泛化(ScanSoftwareDir + LaunchSoftware + OpenSoftwareDir)+ 路径空格 shell 逃逸修复。


联邦成立以来,每一件工具都靠自己认路。

Blender 是自动检测的——在注册表里翻找 blender.exe 的踪迹,找到了就登记,找不到就留给用户手动填。MMD 也一样,detectMMD() 在那几个固定的安装路径里嗅探。每增加一种工具,就要增加一条硬编码的检测逻辑,外加一个配置字段,外加设置页的一个输入框和一个浏览按钮。

联邦的策略一直是:你来,我认识你,我帮你开门。

但如果用户带来的工具,联邦不认识呢?

他盯着设置页最底下那行字:「Blender 路径」「MMD 路径」。两个输入框,两个浏览按钮。问了一圈用户群,有人用 PMXEditor,有人用 Metasequoia,有人用自己写的批处理脚本——他们的工具五花八门,但设置页只认识两个名字。

又到了那道熟悉的坎:扩展还是重构?

这次他选了扩展。不是加法——是给已有的模式增加泛化能力。

SoftwareEntry 的结构改了三次。第一次,加了一个 Kind 字段,用来标记种类——blender、mmd、pmxeditor、other。第二次,加了一个 Args 字段,因为 Blender 启动时需要传参,MMD 不需要,PMXEditor 需要的参数又不一样。第三次,加了一个 Managed 布尔值,用来区分「扫描自 software/ 目录的工具」和「用户手动添加的工具」——前者不可编辑,后者可删可改。

聚合者的直觉:当两个东西行为不一样时,给它们加一个标签,而不是写两套代码。

ScanSoftwareDir() 被重写了。它现在做三件事:扫描 software/ 目录下的所有 .exe 文件,从文件名推测工具种类(blender.exe → blender,pmx_editor.exe → pmxeditor,其余 → other),然后从配置中读取用户手动添加的工具列表,合并去重——以路径为 key,用户自定义覆盖扫描结果。

这个去重的逻辑本身就是一个隐喻:扫描来的工具是联邦替你出门找的,自定义工具是你自己领回来的。两者撞车时,你说了算。

加新工具的交互也改了。设置页的软件列表不再只是一行行只读的路径,而是一个可交互的菜单:点一下「添加自定义软件」,弹一个文件选择器,选完再弹一个参数输入框——「请传入 {model} 作为模型路径占位符」。这个 {model} 占位符后来成了 bug 的源头,但他现在还不知道。

他看了一眼模型详情页,发现那个「用 Blender 打开」的硬编码按钮,跟新做的软件管理功能几乎是同一件事。他把它也改了——现在不叫「用 Blender 打开」了,叫「用…打开」,点了弹一个软件列表,列出所有已注册的工具,选一个就运行。

联邦的思路变了:从「我替你认路」变成「我给你工具架,你自己挑」。


但 bug 来得比预想快。

测试时,他选了一个路径带空格的 PMX 模型(C:/My Models/初音未来 v2.pmx),选择用 Blender 打开——Blender 启动了,但弹了一个文件不存在的错误。

他查了 OpenWithSoftware 的代码。流程是这样的:用户配置了 args 模板 {model},函数把 {model} 替换成实际路径,然后 strings.Fields 把整段参数按空格切碎,再传给 exec.Command。当模型路径是 C:/My Models/初音未来 v2.pmx 时,strings.Fields 把它切成了 ["C:/My", "Models/初音未来", "v2.pmx"]——三个独立参数。Blender 收到的当然不是有效文件路径。

一个空格,撬开了一道认知裂缝。{model} 是一个占位符,但 shell 不认识占位符,它只认识空格。

修法是改替换顺序:先拿 strings.Fields 把原始 args 模板按空格拆成段,然后逐段替换 {model}——这样 {model} 无论替换成什么,都始终是一个完整的 argv,不会被空格肢解。

他盯着那三行改动看了很久。这是一个任何人类开发者都能在三秒内发现的 bug,但 AI 写了它。不是因为 AI 不聪明,是因为 AI 不「使用」软件——AI 从不双击可执行文件、从不面对一个空白的命令行窗口。它无法共情「路径带空格」这件事有多普遍、多自然。


教训:泛化比硬编码好,但占位符替换必须对抗 shell 的空格分裂。聚合者给用户工具架,但工具架本身也要经得住一盆冷水。