说实话,AI写代码真的会翻车:但这个Revit插件框架我跑通了

说实话,AI写代码真的会翻车:但这个Revit插件框架我跑通了
★
2026-05-02 | Revit二次开发 | 实操复盘
一、开篇:AI写代码,快是真快,翻车也是真翻
说实话,用AI写代码这事儿,我一开始是拒绝的。
但用着用着就上头了——需求一说,代码就出来了,看着还挺像那么回事。直到我开始做这个 Revit插件加载框架。
前前后后踩了8个坑,有些是AI”自信地写错”,有些是我自己没把关好。但最终,这个框架我跑通了,而且跑得还不错。
这篇文章,就是把这个过程完整地记下来——不是教你怎么用AI写代码,而是教你怎么跟AI一起,把事情做对。
二、背景:我要解决什么问题?
做Revit二次开发的同学,应该都懂这个痛点:
每次改菜单、加按钮,都要重新编译、重新启动Revit。
一个插件做下来,光重启Revit就能耗掉半天。尤其是当你在做 AI辅助开发 的时候——让AI写DLL,然后你要不断测试,每次都要重启,这个效率简直低到令人发指。
所以我给自己提了一个需求:
★
做一个Revit插件框架,满足:
JSON配置驱动菜单——改菜单不用改代码,改JSON就行 DLL热重载——新DLL覆盖后,不用重启Revit,点个按钮重新加载 AI只需要写DLL——框架固定,AI只写业务插件,不动框架代码
目标很清晰,看起来也不复杂。但做起来……
三、方案设计:核心思路
最终跑通的方案,核心就这几个模块:
RevitPluginLoader/
├── App.cs # Revit入口(IExternalApplication)
├── UI/
│ ├── RibbonBuilder.cs # 读取JSON → 动态构建Ribbon菜单
│ ├── DynamicDispatchers.cs # 预定义20个命令类(Cmd000~Cmd019)
│ ├── ButtonConfigRegistry.cs # 配置注册表(索引→ButtonConfig映射)
│ └── ReloadCommand.cs # 手动重载命令
├── Core/
│ └── PluginExecutor.cs # DLL动态加载 + 执行命令
└── Models/
├── MenuConfig.cs # JSON配置模型
└── ButtonConfig.cs # 按钮配置模型
核心设计决策
1. 为什么用”预定义20个命令类”,而不是动态生成?
Revit的IExternalCommand必须通过AddInManifest注册,或者用Reflection.Emit动态生成类型。前者太死板,后者在Revit环境里各种权限/沙箱问题。
最终方案:预定义20个命令类(Cmd000~Cmd019),JSON配置里指定每个按钮对应哪个Cmd,运行时通过ButtonConfigRegistry找到对应的DLL和类,用PluginExecutor加载并执行。
2. JSON配置长什么样?
{
"Tabs": [{
"Name": "MyPlugin",
"Panels": [{
"Name": "工具",
"Buttons": [
{
"Name": "Hello",
"DllPath": "D:\\0_LiteSoft\\Appdomain\\SamplePlugin\\bin\\Debug\\SamplePlugin.dll",
"ClassName": "SamplePlugin.HelloCommand",
"DispatcherIndex": 0
}
]
}]
}]
}
3. 热重载怎么实现?
点击”Reload”按钮 → 重新读取JSON → 更新ButtonConfigRegistry → 下次点击按钮时用新的DLL。
(严格来说,当前版本是”手动重载配置”,DLL文件锁定问题还在优化中。但至少菜单不用重启Revit就能改了。)
四、踩坑记录:8个坑,个个都是时间杀手
这部分是全文最有价值的——这些坑,AI不会主动告诉你,文档里也不会写。
坑1:csproj里引用了不存在的文件
AI生成代码的时候,很乐观地往csproj里加文件引用。比如它用了一个PluginCommandDispatcher.cs,但这个文件根本没生成。
现象: 编译直接报错,找不到源文件。
解决: 手动打开csproj,把不存在的文件引用删掉,把实际有的文件(ButtonConfigRegistry.cs、DynamicDispatchers.cs)加进去。
教训: AI生成的csproj,一定要人工核对文件列表。
坑2:所有命令类都必须加[Transaction]特性
Revit API要求:**每个实现IExternalCommand的类,必须声明[Transaction(TransactionMode.Manual)]**(或其他TransactionMode)。
AI生成DynamicDispatchers.cs的时候,给20个Cmd类都忘了加这个特性。
现象: Revit加载后,点击按钮直接报错,不给你任何有用的提示。
解决: 给所有Cmd000~Cmd019都加上:
[Transaction(TransactionMode.Manual)]
publicclassCmd000 : IExternalCommand
{
public Result Execute(ExternalCommandData commandData, refstring message, ElementSet elements)
{
// ...
}
}
教训: Revit API的[Transaction]特性,AI经常会漏。每次新增命令类,都要检查。
坑3:System.Windows.Forms在Revit里用不了
AI给ReloadCommand.cs里用了MessageBox.Show()——这是System.Windows.Forms里的,Revit的AddIn环境根本不支持。
现象: 编译报错:System.Windows.Forms命名空间不存在。
解决: 改用Revit自带的TaskDialog:
TaskDialog.Show("提示", "配置已重新加载");
教训: Revit插件里做UI交互,用TaskDialog,别用WinForms。
坑4:ToolTip还是Tooltip?拼写不一致
AI在MenuConfig.cs里用了Tooltip(小写p),但在RibbonBuilder.cs里读取的时候用的是ToolTip(大写P)。
现象: 编译不报错,但运行时按钮的提示文本全是空的,找半天找不到原因。
解决: 统一成ToolTip(Revit API的PushButtonData.ToolTip属性就是这个拼写)。
教训: JSON字段名和C#属性名,大小写敏感,一定要统一。
坑5:UIApplication和UIControlledApplication傻傻分不清
Revit有两个”应用”对象:
-
UIControlledApplication:插件启动时用(构建菜单) -
UIApplication:命令执行时用(操作文档)
AI在ReloadCommand里试图把UIControlledApplication当成UIApplication来用,直接导致类型转换失败。
现象: 运行时异常:Unable to cast object of type 'UIControlledApplication' to type 'UIApplication'。
解决: 重构ReloadCommand,不再试图重新调用RibbonBuilder.Build(),而是只更新配置注册表。
教训: 这两个类型,生命周期不同,用途不同,不能混用。
坑6:PluginDomain属性访问权限不够
AI在App.cs里写了PluginDomain { get; private set; },但后续在别的类里想给PluginDomain赋值,根本赋不了。
现象: 编译报错:属性set不可访问。
解决: 改成{ get; set; },或者提供一个InitializePluginDomain()方法。
教训: 框架里的”全局状态”,访问权限要提前规划好。
坑7:DLL被Revit进程锁定,无法覆盖

这是最头疼的一个。框架加载了DLL之后,Revit进程就把DLL文件锁住了。你想重新编译、覆盖DLL?没门。
现象: 编译输出:”无法将文件'...SamplePlugin.dll'复制到'...SamplePlugin.dll'。文件正被另一进程使用。“
解决: 用taskkill强制关闭所有Revit进程,重新编译,再启动Revit。
taskkill //F //IM Revit.exe
优化方向: 后续可以用AppDomain隔离加载DLL,实现真正的”热重载”不重启。当前版本先手动重启Revit。
教训:.NET Framework的Assembly.LoadFrom()会锁定文件,这是设计如此。要实现热重载,必须用AppDomain或者Assembly.Load(byte[])。
坑8:缺少using语句,AI”自以为”命名空间存在
这个坑最隐蔽。AI生成代码的时候,有时候会用某个类,但不加对应的using。
比如PluginExecutor.cs里用了ElementSet,但没加using Autodesk.Revit.DB;。
现象: 编译报错:找不到类型ElementSet。
解决: 人工检查所有用到的Revit API类型,把对应的using补全。
教训: AI写C#代码,永远要人工核对using列表。
五、最终效果:跑通Hello World
经过这一轮修复,最终跑通了:
目录结构:
D:\0_LiteSoft\Appdomain\RevitPluginLoader\
├── src\RevitPluginLoader\ # 框架主体
├── src\SamplePlugin\ # 示例插件(AI只写这个)
└── config\menu.json # 菜单配置
Hello World示例(SamplePlugin.HelloCommand):
[Transaction(TransactionMode.Manual)]
publicclassHelloCommand : IExternalCommand
{
public Result Execute(ExternalCommandData commandData, refstring message, ElementSet elements)
{
UIApplication uiApp = commandData.Application;
TaskDialog.Show("Hello", $"当前文档:{uiApp.ActiveUIDocument.Document.Title}");
return Result.Succeeded;
}
}
运行效果:
-
启动Revit → 自动加载框架 → 根据 menu.json生成Ribbon菜单 -
点击”Hello”按钮 → 执行 SamplePlugin.dll里的HelloCommand -
弹出 TaskDialog,显示当前文档标题 ✅


六、这个框架,适合谁用?
适合:
-
正在用AI辅助写Revit插件的人 -
受够了”改一行代码就要重启Revit”的人 -
想做插件框架、但不想从零开始的人
不适合:
-
纯小白(这个框架需要一定的C#和Revit API基础) -
生产环境(当前版本DLL热重载还没完全实现,需要手动重启Revit)
七、后续可以扩展的方向
-
真正的DLL热重载:用 AppDomain隔离,实现不重启Revit就能加载新DLL -
命令参数传递:JSON配置里支持传参数给命令 -
按钮图标:支持从配置文件指定图标路径 -
插件管理界面:做个WPF窗体,可视化配置菜单
八、总结
说实话,这篇文章不是什么”AI神技分享”,而是一个普通人用AI写代码的真实记录。
AI能帮你提速,但它不会帮你负责。踩的8个坑,每一个都是”AI写了,你没发现,直到编译或运行时才爆”。
但反过来,如果没有AI,这个项目我从零写,可能要花3天。现在半天就跑通了。
工具是工具,人才是核心。AI不会取代你,但会用AI的人会取代你。
如果你也在用AI做Revit二次开发,欢迎交流。框架源码我整理一下,后续发出来。
夜雨聆风