简介:面向仍在使用 VC6.0 的开发者与维护老项目的技术人员,这份资源针对 Windows 7/10 环境下 VC6 无法打开文件、不能添加工程文件的常见故障,提供 FileTool 附加项插件及完整编译材料,帮助恢复“文件”菜单相关操作。压缩包共 51 个文件,约 4.9MB,包含 cpp/h 源码、dsp/dsw 工程文件、rc 资源与 bmp 工具栏位图,以及 obj、pdb、lib、exp、tlb 等编译中间件和 Debug、Release 下已生成的 dll,既可直接取用,也便于按需重新编译排查。已有 447 人学习关注。读者可获得可注册的 FileTool.dll、可复现的 VC6 插件工程和编译产物,并通过附加项加载方式完成工具栏集成;对需要兼容旧版 IDE、恢复工程文件操作或研究 VC6 插件机制的人来说,这份材料兼顾直接可用性与二次开发参考价值。
1. Win10 上还在维护 VC6 工程,缺的其实是一个「添加文件」入口
上周帮人收拾一个 2004 年立项的 MFC 工程,编译链还是 VC6 加 SP6。代码本身不复杂,难受的是每天要做几十遍的动作:写好一个 .cpp,把它塞进工程。Project → Add To Project → Files,弹出来一个老式模态对话框,在 Win10 上要么转半天,要么路径框里全是上次的残留,要么干脆闪一下没下文。这时候就会想起 FileTool。vc6.0 FileTool.dll 这套东西在 win10/win7 上折腾的核心,就是把「添加工程文件」这一步从三级菜单加模态对话框,压缩成工具栏上一个按钮或者菜单栏里一行命令。它适合谁?还在给老工程打补丁、做增量维护的人;不适合纯新项目——新项目直接上现代 IDE,没必要为了一个按钮回到 1998 年的交互。这一篇把我自己反复装、反复翻车的链路摊开写:DLL 怎么注册、菜单怎么挂上去、Win7/Win10 下为什么明明注册成功 IDE 却不认、工程文件被改坏之后怎么退回来。
2. FileTool 到底动了什么:从 IDE 插件模型到工程文件写入
2.1 一个 DLL 凭什么能改菜单栏
VC6 的 IDE 支持两类扩展:一类是宏(.dsm,VBScript 写的),一类是加载项(Add-in,编译成 .dll 的 COM 组件)。FileTool 属于后者。它实现的是 IDE 规定的加载项接口,进程内 COM 服务器,注册之后 IDE 启动时会去注册表里翻一个固定的位置,把里面登记过的加载项逐个实例化。
关键点在这里:加载项被实例化时会拿到一个 Application 对象,也就是常说的 DTE。拿到这个对象之后,它能干的事情包括往 IDE 里注册新命令(Commands.Add)、把命令挂到某个工具栏或菜单上(CommandBars)、读取当前活动工程和当前打开文档。FileTool 做的加法很朴素——注册一两个命令,一个负责「把当前编辑器里对应的文件加进活动工程」,一个负责「打开文件」。所以你在菜单栏或工具栏上看到的那个按钮,本质是一个 COM 命令的入口,跟 IDE 自带的 Project 菜单项地位相同,只是后台实现换成了插件。
理解这一层,后面很多现象就顺了:按钮灰不灰,取决于插件有没有被加载;命令点了没反应,往往是活动工程这个上下文没拿到;而文件到底进没进工程,跟你点没点按钮没关系,只跟磁盘上那份 .dsp 有没有被改写有关系。调试的时候要分清「UI 层」和「文件层」,别一看见按钮没出来就去怀疑 DLL 坏了。
VC6 的工程文件是纯文本。一个 .dsp 里,源文件是按块罗列的:
# Begin Group "Source Files" # Begin Source File SOURCE=.\src\worker.cpp # End Source File # End Group所谓「添加文件到工程」,插件做的事情就是在这个结构里插入一段# Begin Source File/SOURCE=.../# End Source File,然后通知 IDE 刷新工程树。它同时可能会碰 .dsw(工作区,记录工程归属)、.ncb(浏览数据库,类视图用的)、.opt(IDE 本地状态,一般不入库)。其中 .ncb 是二进制的,坏了直接删,IDE 会重建;.dsp 是文本,坏了要能人工退回去。这两点的处理方式完全不同,后面单独讲。
还有一层容易忽略:插件写入 .dsp 用的是相对路径,通常带.\前缀,相对于 .dsp 所在目录。如果你添加的是工程目录之外的文件,写进去的可能是..\..\shared\foo.cpp这种形式。这在单机上没问题,一旦工程目录整体挪位置或者被另一台机器拉走,相对路径就对不上,工程树里会出现带红色感叹号的条目。这不是插件的 bug,是相对路径的固有代价。
2.2 装它只有三个动作,但顺序不能乱
整套安装可以压缩成三步:把 DLL 放到 IDE 约定的加载项目录、注册这个 COM 组件、在 IDE 的加载项对话框里勾上它。三步的顺序不能颠倒,因为第三步的列表是从注册表读的,注册没成功,列表里就是空的,你在界面上怎么找都找不到。
先确认目录。VC6 的安装根目录下有两个关键子目录:VC98(编译器、库、头文件)和Common\MSDev98(IDE 本体和扩展)。加载项 DLL 应该放进Common\MSDev98\AddIns。这个目录不一定存在,不存在就自己建。放错地方不会报错,只会表现为「注册成功但 IDE 找不到」——因为 IDE 只查注册表,不扫目录,而很多人以为放进去就完事了。
# 设两个变量,后面所有命令都引用它们,避免路径写死后改起来到处找 VC6_ROOT="C:/Program Files (x86)/VC6" # 替换成你自己的 VC6 安装根目录 ADDINS_DIR="$VC6_ROOT/Common/MSDev98/AddIns" # 目录可能不存在,先建 mkdir -p "$ADDINS_DIR" # 把 FileTool.dll 复制进去(假设 dll 就放在当前目录) cp ./FileTool.dll "$ADDINS_DIR/" # 列出确认,文件确实到位了再往下走 ls -l "$ADDINS_DIR/FileTool.dll"这段做的事情很少,但每一步都有必要。VC6_ROOT抽出来是因为后面注册表、备份、脚本都要引用同一个根;mkdir -p是因为AddIns在精简安装里经常缺失;最后那次ls是给自己一个确认点,避免后面对着一个根本没到位的文件排查半小时。
注册这一步在 64 位系统上有坑。VC6 是 32 位程序,FileTool.dll 是 32 位 COM 组件,必须用 32 位版本的注册工具。在 64 位 Windows 上,从命令行直接敲regsvr32,命中的是 System32 下的 64 位版本,它会告诉你「模块已加载,但找不到入口点 DllRegisterServer」,或者干脆报 0x80070005。正确做法是显式走 SysWOW64:
# 64 位系统上注册 32 位 COM 组件,必须用 SysWOW64 下的版本 # 而且要以管理员身份运行这个终端,否则写注册表会失败 "C:/Windows/SysWOW64/regsvr32.exe" "C:/Program Files (x86)/VC6/Common/MSDev98/AddIns/FileTool.dll" # 注册成功会弹一个对话框。如果没弹,用 /s 静默模式配合 echo 看返回码 "C:/Windows/SysWOW64/regsvr32.exe" /s "C:/Program Files (x86)/VC6/Common/MSDev98/AddIns/FileTool.dll" echo "exit code: $?"返回码 0 表示注册函数执行成功,但这只说明 COM 那一侧的 CLSID 写进去了,不代表 IDE 就会加载它。IDE 认的是另一套键,两套键是分开的,这是绝大多数人第一次装失败的根本原因。
第三步在 IDE 里。打开 Tools → Customize,切到 Add-ins and Macro Files 标签页,列表里应该出现 FileTool 相关的一项,前面有个复选框,勾上,关掉对话框。这一步做的是把LoadBehavior从 0 改成 1,也就是「随 IDE 启动自动加载」。如果列表里空着,别在界面上继续折腾了,回去查注册表,见 3.1。
2.3 把命令挂到菜单栏和工具栏上
加载项勾上之后,不一定立刻就有按钮。有些版本会自己弹一个浮动小工具条,有些什么都不显示,命令躺在命令列表里等你手工挂。我一般直接手工挂,位置可控,重装也不用重新找。
路径是 Tools → Customize → Commands 标签页。分类下拉里找加载项相关的分类(不同版本叫法不一样,有的归在 Add-ins 下,有的单独一栏),右边是命令列表。找到那个名字里带 FileTool 或 Add File 的项,用鼠标按住,直接拖到主菜单栏上你想放的位置,松手。菜单项就出来了。同样的操作可以把命令拖到任意一个已显示的工具栏上,变成一个按钮。
拖完之后别急着关对话框,切到 Keyboard 标签页,在同一个分类里选中这条命令,把光标放进 Press new shortcut key 框,按一个你顺手的组合。这里有个血泪经验:别用 Ctrl+A、Ctrl+F 这类被编辑器占满的组合,VC6 的快捷键优先级处理得比较粗暴,冲突时谁生效看加载顺序,很玄学。我一般用 Ctrl+Shift+A 这种三段式的,基本不会撞。
挂好之后做一次验证:随便打开一个 .cpp,把焦点放在编辑区,按你设的快捷键,然后回到 FileView 看工程树有没有多出节点。如果菜单项是灰的,说明命令注册上了但当前上下文不满足(比如没有活动工程);如果能点但没反应,去看 .dsp 的修改时间有没有变——没变就是命令执行时抛异常被吞了,多半是权限或者路径问题。
3. 手工复现一遍:从 DLL 注册到菜单项可点击的完整链路
3.1 注册表这两处,缺一处都不行
COM 组件的注册和 IDE 加载项的登记是两套东西。前者是 DllRegisterServer 干的,写的是 CLSID、ProgID、InprocServer32 这些;后者是安装程序或者手工写的,位置在HKCU\Software\<IDE 厂商键>\DevStudio\6.0\AddIns\<加载项 ProgID>。IDE 启动时只遍历后面这个位置,把每个子键当成一个候选加载项,读里面的Description和LoadBehavior。
所以排查顺序永远是:先确认 COM 注册成功,再确认加载项键存在且LoadBehavior不为 0。
# 第一步:确认 COM 那侧注册成功。CLSID 每个 dll 不同, # 更省事的办法是按 ProgID 反查,FileTool 的 ProgID 一般形如 FileTool.Connect reg query "HKCR\FileTool.Connect\CLSID" /ve # 第二步:确认 IDE 的加载项键存在 reg query "HKCU\Software\<IDE 厂商键>\DevStudio\6.0\AddIns\FileTool.Connect" /v LoadBehavior reg query "HKCU\Software\<IDE 厂商键>\DevStudio\6.0\AddIns\FileTool.Connect" /v Descriptionreg query的返回如果是「系统找不到指定的注册表项或值」,就说明这一层没写进去。注意上面路径里的<IDE 厂商键>需要换成本机实际的那一级,不同安装来源可能不一样,用reg query "HKCU\Software" /f DevStudio /k模糊搜一下就能定位到。
LoadBehavior的取值语义:0 是不加载,1 是随 IDE 启动加载,8 是命令行触发才加载,16 是按需加载。绝大多数情况下要的是 1。如果注册成功、键也在,但值是 0,手工补一条:
# 手工把加载行为改成 1(启动时加载) reg add "HKCU\Software\<IDE 厂商键>\DevStudio\6.0\AddIns\FileTool.Connect" ^ /v LoadBehavior /t REG_DWORD /d 1 /f # 顺手把描述写上,否则加载项对话框里那一行是空的,勾选的时候容易懵 reg add "HKCU\Software\<IDE 厂商键>\DevStudio\6.0\AddIns\FileTool.Connect" ^ /v Description /t REG_SZ /d "FileTool Add-in" /f/f是强制覆盖,加上它免得交互确认。两条命令执行完,完全关掉 IDE 再开,Add-ins 列表里应该就有内容了。这里插一句:改注册表之前先导出备份,reg export "HKCU\Software\<IDE 厂商键>\DevStudio\6.0" devstudio_backup.reg,一条命令的事,出问题能整段退回来,比手工对着键值猜快得多。
3.2 三种验证手段,从粗到细
装完之后我一般做三层验证,逐层往下,哪层断了就知道问题在哪。
第一层,进程里有没有加载这个 DLL。开 IDE,然后看模块列表。最土的办法是用任务管理器的「打开文件位置」那套不行,得用能列模块的工具。简单点,直接看 DLL 能不能被独占重命名——如果 IDE 真的把它加载了,这个 DLL 会被进程锁住,你重命名会失败。
第二层,IDE 的 Add-ins 对话框里那一项有没有被勾上。这个最直观,勾上了就说明 IDE 读到了注册表并且愿意加载。注意这个勾选状态本身也写在注册表里,退出 IDE 时落盘,如果 IDE 是非正常退出(崩溃、强杀),这个状态可能丢。
第三层,命令能不能执行。这层要看 .dsp 的修改时间:
# 记录执行命令前 .dsp 的时间戳和大小 stat -c '%y %s %n' ./project.dsp # 在 IDE 里按快捷键执行一次「添加文件」 # 再取一次,对比 stat -c '%y %s %n' ./project.dsp时间变了说明插件确实动了文件,功能链路是通的,剩下的只是 UI 好不好用的问题;时间没变说明命令没走到写文件那一步,往权限、活动工程上下文、文件是否只读这三个方向查。
提示:验证阶段建议拿一个练手工程,别拿生产工程当小白鼠。插件的写入逻辑对 .dsp 结构是有假设的,格式奇怪的老工程可能被插到错误的分组里。
3.3 用脚本核对工程里到底多了什么
肉眼对 .dsp 是不可靠的,尤其工程里有几百个文件的时候。用脚本把 SOURCE 条目抽出来做集合差,几十行的事,长期收益很高。
# -*- coding: utf-8 -*- """抽取 .dsp 中的 SOURCE 条目,用于前后对比或跨版本核对""" import re import sys # VC6 的 .dsp 通常是 ANSI/GBK 编码,路径里可能有中文 ENC = "gbk" def collect_sources(dsp_path): with open(dsp_path, "r", encoding=ENC, errors="replace") as f: text = f.read() # 只在 SOURCE= 行上匹配,忽略 OTHER_FILES / USERDEP 之类的其它键 # .dsp 里的路径用反斜杠,统一成小写比较,避免大小写差异造成假新增 pattern = re.compile(r"^SOURCE=(.+?)\s*$", re.MULTILINE) return {m.group(1).strip().lower() for m in pattern.finditer(text)} def main(old_dsp, new_dsp): old = collect_sources(old_dsp) new = collect_sources(new_dsp) added = sorted(new - old) removed = sorted(old - new) print(f"旧工程条目数: {len(old)}") print(f"新工程条目数: {len(new)}") print(f"新增 {len(added)}:") for p in added: print(f" + {p}") print(f"减少 {len(removed)}:") for p in removed: print(f" - {p}") if __name__ == "__main__": if len(sys.argv) != 3: sys.exit("用法: python diff_dsp.py old.dsp new.dsp") main(sys.argv[1], sys.argv[2])encoding="gbk"这一行是必须的。.dsp 是 VC6 时代的产物,中文 Windows 下存的是本地代码页,用 utf-8 打开会抛 UnicodeDecodeError,加了errors="replace"也只是勉强能跑但路径会乱。^SOURCE=前面的^和re.MULTILINE保证只匹配行首,避免误伤USERDEP=里出现的同名字符串。统一转小写是为了绕开 Windows 路径大小写不敏感带来的假差异——这一点在实际比对里非常容易踩坑,同一批文件换个写法就能报出十几个「新增」。
跑法就是先备份一份 .dsp,操作完再跑一次:
cp project.dsp /tmp/project.dsp.bak python diff_dsp.py /tmp/project.dsp.bak ./project.dsp输出里只应该有你想加的那几个文件。如果多出来别的,说明插件把备份文件、或者自动生成的临时文件也当成源文件收进去了,这种要手工从 .dsp 里删掉。
4. 避坑与排查:Win7/Win10 下装不上的五种现场
老工具在新系统上的问题,绝大多数不是「坏了」,而是「权限模型变了」或者「路径规则变了」。下面五条是我这些年重复遇到过最多次的,按发生频率排。
4.1 注册时弹「找不到入口点 DllRegisterServer」
现象:命令行敲完 regsvr32,弹一个红叉对话框,说模块已加载但找不到 DllRegisterServer 入口点,或者干脆报 0x80070005 拒绝访问。
原因有两个,经常同时出现。第一是位数错配:在 64 位 Windows 上,默认 PATH 里的regsvr32指向 System32 下的 64 位版本,它去加载一个 32 位 DLL,PE 头对不上,表现就是「模块已加载但……」。第二是权限:即便位数对了,DllRegisterServer内部要写 HKLM,普通权限下会返回拒绝访问,而这个错误经常被包装成上面那句话。
解决:显式用C:\Windows\SysWOW64\regsvr32.exe,并且以管理员身份启动终端。判断 DLL 位数不用工具,看 PE 头或者直接试——32 位进程加载失败会有明确的错误码 193(不是有效的 Win32 程序),这个比 0x80070005 更好认。
4.2 注册成功了,Add-ins 列表里还是空的
现象:regsvr32 弹了成功提示,但 IDE 的 Customize → Add-ins and Macro Files 里什么都没有。
原因:注册成功只代表 COM 那一侧写进去了(CLSID、InprocServer32、ProgID),IDE 读的是另一套键。两套键之间的桥是安装程序负责搭的,手工注册 DLL 不会自动补。另外还有一种情况是键写到了 HKLM 而 IDE 读的是 HKCU,或者反过来,取决于 IDE 以什么权限运行。
解决:按 3.1 的方法,先reg query "HKCR\FileTool.Connect\CLSID"确认 COM 那侧在,再查 HKCU 下DevStudio\6.0\AddIns有没有对应子键。没有就手工建,至少要有Description(字符串)和LoadBehavior(DWORD,值 1)两个值。建完完全退出 IDE 重开,不是关窗口,是确保进程结束。
4.3 勾上了,但一启动 IDE 就报加载项加载失败
现象:IDE 启动变慢,或者弹一个对话框说某个加载项无法加载,有时候直接卡死在启动画面。
原因:最常见的是权限级别不一致。VC6 在 Win10 上很多人会设成「以管理员身份运行」加兼容模式,而 DLL 是在普通权限下注册的,COM 在跨完整性级别激活时会失败。第二个原因是杀软把老式 COM 组件当可疑行为拦了。第三个原因是 DLL 依赖的运行时库缺失,老组件经常依赖某个特定版本的 CRT。
解决:先统一权限级别,注册和运行 IDE 用同一档权限,要么都是管理员,要么都是普通用户,别混。然后临时关掉实时防护试一次,能过就说明是拦截。最后用依赖查看工具看看这个 DLL 静态依赖了什么运行库,缺什么补什么。真搞不定就退回到「不用插件、手工改 .dsp」的方案,见第 5 章。
4.4 菜单项能点,但文件没进工程
现象:快捷键按了,菜单项也点了,工程树没有任何变化,或者只多了一个空的节点。
原因:按概率排,第一是活动工程不对——一个 workspace 里挂了好几个工程,插件取的是「当前活动」那个,你以为在改 A,实际改的是 B。第二是文件已经在工程里了,插件做了去重直接跳过。第三是 .dsp 只读,或者被版本管控插件锁住,写入失败但错误被吞掉没有提示。
解决:先在 FileView 里点一下目标工程,确认它变成活动工程(图标会变粗),再执行命令。然后全工程搜一下那个文件名,看是不是已经存在。最后看 .dsp 的文件属性,把只读去掉,同时确认没有别的进程占着它——用handle或者资源监视器看谁持有句柄。
4.5 自定义的菜单和工具栏,重启 IDE 就消失
现象:辛苦拖上去的菜单项,关掉 IDE 再打开就没了,每次都要重拖一遍。
原因:VC6 把 UI 自定义状态存在注册表里的一个位置,退出时写入。如果 IDE 是崩溃退出、被强杀、或者以受限权限运行导致写不进去,下次启动读到的还是旧状态。另外工程目录如果被设成只读共享,某些配置也可能落不了盘。
解决:正常关闭 IDE,等进程完全退出(任务管理器里确认没有残留),再重开看还在不在。如果还是丢,就以管理员身份运行一次 IDE,把自定义做完,正常退出,让配置能写进注册表,之后再用普通权限开。养成习惯:动完 UI 配置,先退出重开验证一次,别等下次开工才发现白干。
5. 工程文件被改坏之后:.dsp 的结构与回滚方法
5.1 .dsp 的分组结构和插入位置
VC6 的 .dsp 是分组的,每个组用# Begin Group "组名"和# End Group包起来,源文件块嵌在组里。常见的组有 Source Files、Header Files、Resource Files,还有自定义组。插件往里加文件,应该加到对应的组里;加错组不会导致编译失败,但工程树会乱,时间长了没人分得清哪些文件在哪个组。
除了组结构,还有几类行要认:# PROP开头的是工程属性(输出目录、目标类型、依赖库),# ADD CPP/# ADD LINK32是编译链接选项,USERDEP是用户自定义依赖。手工编辑 .dsp 的时候,只在# Begin Source File块上做增删,别碰# PROP行——改错一个属性,工程能编但产物不对,比编不过更难查。
注意一个细节:文件块的结尾必须严格是# End Source File,中间不能夹空行以外的任何东西。我在一次事故里见过有人在SOURCE=行后面加了行注释,结果 IDE 打开时静默丢掉了那一条,也不报错,等到链接时才发现少了一个目标文件,查了很久。
5.2 用文本比对做增量回滚
回滚的正确姿势不是「用备份整个覆盖」,而是先看清插件到底改了哪些行,再决定回退范围。因为你在操作前后可能还顺手改过别的配置,整份覆盖会把那些改动一起冲掉。
# 用 diff 看行级差异,-u 输出上下文,方便判断改动落在哪个组里 diff -u /tmp/project.dsp.bak ./project.dsp > /tmp/dsp.diff # 只看差异的行数统计,快速判断改动规模 diff /tmp/project.dsp.bak ./project.dsp | grep -c '^[<>]' # 确认只多了预期的块,就保留;否则精确回退某几行判断标准很直白:差异行里应该只有# Begin Source File、SOURCE=...、# End Source File这三种,加上可能的分组空行。出现别的,比如某条# PROP变了,说明插件或者别的什么东西动了不该动的地方,这时候要按行回退而不是整份覆盖。
整份覆盖适合一种场景:工程已经彻底乱了,你自己也说不清改了多少。这时候cp回备份,然后用 3.3 的脚本对比「当前 .dsp」和「备份 .dsp」,把这段时间里真正需要保留的改动重新做一遍。慢,但可控。我一般的习惯是把 .dsp 和 .dsw 一起放进版本库,每次动工程配置前提交一次,回滚就是一条git checkout -- *.dsp,比任何手工方案都快。
5.3 批量导入:脚本比手工点更靠谱
如果一次要加几十个文件,插件一个个点效率太低,而且每点一次就多一次出错机会。这时候直接改 .dsp 更快。
# -*- coding: utf-8 -*- """往 .dsp 的指定分组里批量插入源文件块,带重复检测和备份""" import os import re import shutil import sys ENC = "gbk" def add_files(dsp_path, group_name, files): # 先备份,出问题一条 cp 就能退 backup = dsp_path + ".bak" shutil.copy2(dsp_path, backup) with open(dsp_path, "r", encoding=ENC, errors="replace") as f: text = f.read() # 已存在的条目,避免重复插入(同样统一小写比较) existing = {m.group(1).strip().lower() for m in re.finditer(r"^SOURCE=(.+?)\s*$", text, re.MULTILINE)} # 定位目标分组的结束位置 # End Group group_pat = re.compile( r'(# Begin Group "%s".*?)(# End Group)' % re.escape(group_name), re.DOTALL) m = group_pat.search(text) if not m: sys.exit(f"没找到分组: {group_name}") block_lines = [] for fp in files: # .dsp 里统一用相对路径 + 反斜杠,且以 .\ 开头 rel = ".\\" + fp.replace("/", "\\").lstrip(".\\") if rel.lower() in existing: print(f"跳过(已存在): {rel}") continue block_lines.append("# Begin Source File\n\nSOURCE=%s\n# End Source File\n" % rel) print(f"新增: {rel}") if not block_lines: print("没有需要新增的文件") return # 插在 # End Group 之前,块之间用一个空行隔开 new_body = m.group(1) + "\n" + "\n".join(block_lines) + m.group(2) text = text[:m.start()] + new_body + text[m.end():] # 写回时保持 GBK,不能写成 utf-8,否则 IDE 打开中文路径会乱码 with open(dsp_path, "w", encoding=ENC, newline="\r\n") as f: f.write(text) print(f"完成,原文件已备份到 {backup}") if __name__ == "__main__": # 用法: python add_sources.py project.dsp "Source Files" src/a.cpp src/b.cpp add_files(sys.argv[1], sys.argv[2], sys.argv[3:])几个参数值得说明。group_name必须和 .dsp 里的字符串完全一致,包括大小写和空格,写错直接退出而不是瞎猜一个组塞进去。newline="\r\n"是必须的,.dsp 用 CRLF 换行,用 LF 写回虽然 IDE 大多能读,但混着来会让 diff 变得没法看。相对路径的构造用lstrip(".\\")去掉可能带的前导.\,避免生成.\...\src\a.cpp这种双前缀。重复检测放在插入前,比插入后再去重安全得多。
跑完之后一定要用 3.3 的脚本核对结果,再开 IDE 看工程树。别直接编译——工程树对了但结构错位的话,编译能过,但下一次插件操作可能就崩在解析上。
6. 进阶:把这一套固化成开工前的固定动作
折腾到这个程度,插件本身已经不是重点了,重点是别让它把你坑进去。我现在动任何老工程之前,固定走三步,总共不到两分钟,但省下的时间以小时计。
| 动作 | 命令 / 操作 | 目的 |
|---|---|---|
| 备份工程文件 | cp project.dsp project.dsp.bak && cp project.dsw project.dsw.bak | 出问题一条 cp 退回 |
| 记录基线 | python diff_dsp.py把当前 SOURCE 集合存成基线文件 | 后续任何变更都能做集合差 |
| 清浏览数据库 | 关闭 IDE 后删掉 .ncb | 类视图错乱时不用猜 |
第一行的备份别嫌土。.dsp 和 .dsw 加起来通常不到 100KB,备份成本可以忽略,但没有备份的时候,一次错误的分组插入能让人对着一千多行的文本找半天。.ncb那个更简单——它是纯缓存,删了 IDE 会重建,遇到类视图里出现幽灵类、跳转跳到不存在的行、符号搜索卡死,第一反应就是关 IDE 删 .ncb,九成能解决。
第二行的基线我一直保留,原因是有一次插件把一个自动生成的中间文件当成源文件塞进了工程,编译没报错,直到打包发布时才发现产物里多了一堆不该有的东西。从那以后我每次用 FileTool 加完文件,都会强制跑一遍集合差,确认新增的条目和预期完全一致再往下走。这个习惯看起来啰嗦,但它把「插件到底干了什么」从一个黑匣子变成了一份可读的差异清单。
还有一个顺手的小技巧:把 FileTool 的命令快捷键设成 Ctrl+Shift+A,然后在自己常用的几个工程里各试一次,确认不会和输入法、和 IDE 自带的全选、和某个输入辅助工具撞车。撞车的表现很隐蔽——按下去没反应,你以为是插件没装好,其实是快捷键被别的程序截走了。真遇到了,换个组合比排查半天快。
最后一句实话:这套东西的价值不在于「有个按钮」,而在于它把一个高频动作的路径缩短了。缩短路径的前提是你知道它改了哪个文件、改成了什么样、改坏了怎么退。这三件事都想清楚了,插件才是工具;想不清楚,它就是个随时会咬人的黑匣子。希望帮到你。
本文还有配套的精品资源,点击获取