简介:面向Revit 2020二次开发者的RevitLookup表格查找工具包,内含可直接部署的插件注册文件与完整程序源码。RevitLookup用于查看和检索Revit数据库中的元素、参数与关系,帮助开发者直观检查接口数据结构、定位插件问题,是实施Revit二次开发时常用的调试辅助工具。压缩包共161个文件,以78个C#源码文件为主,另含编译好的动态库、addin注册文件、resx资源文件以及多种图标素材,整体仅1.05MB;源码工程、配置与图像资源彼此配套,按模块分离,适合阅读实现脉络。已有1652人学习下载。工具包兼顾开箱即用和源码修改两种方式,既满足日常调试需要,也适合在Visual Studio中调整项目引用后重新编译,以便匹配本机环境,并进一步按业务场景定制查找功能。
1. RevitLookup 2020:把Revit API的黑匣子撬开,二开调试从盲猜变成翻树
写过Revit插件的朋友多半有过这种经历:自己写的命令跑完没报错,但构件属性没变,或者拿到一个Element却不知道里面到底挂了多少参数、几何数据长什么样。这时候最笨的办法是往代码里临时塞TaskDialog.Show()一行行打日志,打完了再删,删完发现还有别的要查,又得重新编译一遍。RevitLookup 2020就是来终结这种盲猜的:它是一个开源的外部应用,以addin清单文件的形式挂进Revit 2020,装上之后你可以在Revit界面里直接"窥探"任意元素、任意文档对象,把API对象的属性、参数、几何、依赖关系一层层展开成树。它尤其适合两类人:一类是刚接触Revit二次开发、连Parameter和ElementId都没摸熟的新手,另一类是写复杂逻辑时被API文档绕晕、需要立刻确认对象真实结构的熟手。源码就在本地,顺着编译一遍,你还能按自己的需求改菜单、加命令。
2. 先从成品入手:addin加载、菜单入口和Snoop的三个用法
2.1 addin文件是什么:Revit 2020只认这一份XML清单
Revit本身不扫描DLL,它只扫描后缀为.addin的XML清单文件,再由清单里写的Assembly路径去定位你的插件程序集。Revit 2020里,addin文件放在两个位置:程序数据级C:\ProgramData\Autodesk\Revit\Addins\2020,对所有Windows用户生效;用户级C:\Users\<用户名>\AppData\Roaming\Autodesk\Revit\Addins\2020,只对当前用户生效。我一般优先放用户级,避免在公司电脑上因为没有ProgramData写权限而加载失败。
RevitLookup编译产物是一个DLL,配套的addin内容大致是这样:
<?xml version="1.0" encoding="utf-8"?> <RevitAddIns> <AddIn Type="Application"> <Name>RevitLookup 2020</Name> <Assembly>C:\LookupOutput\RevitLookup.dll</Assembly> <FullClassName>RevitLookup.App</FullClassName> <ClientId>8D2A8C4F-0000-4000-8000-123456789ABC</ClientId> <VendorId>ADSK</VendorId> <VendorDescription>RevitLookup for Revit 2020</VendorDescription> </AddIn> </RevitAddIns>这份清单里有三个关键点。Type必须写Application而不是Command,因为RevitLookup的入口是IExternalApplication,它要在Revit启动时注册菜单;FullClassName要和你源码里的入口类完全一致,包括命名空间,写错一个字加载就会静默失败;ClientId是一个GUID,不能和本机其他插件重复,重复的后果是Revit只加载其中一个。把这段XML存成RevitLookup.addin,编码建议用UTF-8带BOM或者ANSI,纯UTF-8无BOM在某些中文系统上会被误读,这是addin加载失败里最玄学的一条,后面避坑章节还会再提。
2.2 加载之后去哪找:Developer选项卡与入口菜单
addin放好、Revit 2020启动后,默认界面里看不到任何入口。很多人以为加载失败,其实Revit把外部应用的工具藏在了"附加模块"里。正确做法是:打开Revit,点左上角文件菜单→选项→界面,在"工具"分组里勾上Developer(开发工具)。确定后,界面上方会多出一个Developer选项卡,RevitLookup的菜单就挂在这里。
展开后你会看到一组以Snoop开头的命令。Snoop直译是"窥探",它干的事就是把Revit内存里的对象抓出来,用树形结构展示给你看。这个窗口和Visual Studio的调试器"局部变量"窗口很像,但它是独立于代码运行的,哪怕你的插件已经关闭,只要Revit进程还开着,就能继续查对象。初次打开Snoop窗口时,左侧是对象层级,右侧是当前选中节点的属性明细,底部的搜索框支持按名称过滤成员,查找海量属性时非常管用。
2.3 Snoop Current / Snoop DB / Snoop Pick 三种调试路径怎么选
RevitLookup在2020版本里最常见的三个入口,对应三种完全不同的使用场景。
Snoop Current Selection(当前选择): 先在Revit视图里选中一个墙体、族实例或者标高,再点这个命令,它会直接定位到你选中的Element。这是日常用得最多的入口,查"这个构件到底有什么参数""它的Location是LocationCurve还是LocationPoint"都靠它。
Snoop DB(数据库): 不看具体构件,而是看Revit文档本身。它从Document开始展开,你能翻到ElementSet`、ParameterSet、项目信息、族列表等文档级对象。适合查"当前文档里有哪些族""项目的ProjectInfo`参数被塞了什么值"这类整库问题。缺点是树很大,加载略慢,展开时要有点耐心。
Snoop Pick(点选): 点击命令后光标变成拾取状态,你在视图中点哪个构件,它就Snoop哪个构件,不用提前选中。在深嵌套的族里一层层点进去查内部几何时,比先选中再切换命令顺手得多。
我的习惯是:明确要查某个选中构件用Current,不确定对象在哪就用Pick,怀疑是文档级数据问题就直接上DB。三个入口背后的Snoopable对象模型是同一套,区别只是起点不同,所以你从哪个入口进去,最终看到的结构都一样。
3. 源码编译:从仓库拉到F5能跑,差在哪几步
3.1 项目结构:sln、一个主项目、两个Revit引用
把RevitLookup 2020的源码下载下来解压之后,你最先看到的是一个sln解决方案文件和一个主项目文件夹。整个源码的组织并不复杂:核心逻辑是一个类库项目,入口是IExternalApplication,Snoop窗口用的是Windows Forms,没有依赖第三方UI框架,所以还原起来比很多商业插件省心。
打开项目文件后,第一件事是检查引用。Revit二次开发的项目只需要两个关键程序集:RevitAPI.dll和RevitAPIUI.dll。前者管文档、元素、几何等底层对象,后者管UI、UIDocument、选择集。RevitLookup两个都要用,所以都会引用。如果你的项目里引用的DLL版本和本机Revit 2020不一致,编译能过但一加载就报"试图加载格式不正确的程序集"或API版本不匹配,这一条值得记进避坑手册。
3.2 引用环境:HintPath、目标框架、x64
打开csproj文件,重点检查三处配置。第一处是目标框架,Revit 2020基于.NET Framework 4.7.2运行,所以TargetFrameworkVersion要对应v4.7.2或更低(比如4.6.1也能跑),但不要选netcore或net5.0以上,Revit 2020不认识。第二处是平台目标,必须x64,Revit 2020只有64位版本,你把项目设成AnyCPU在运行时也可能踩雷。第三处是引用路径:
<Reference Include="RevitAPI"> <HintPath>C:\Program Files\Autodesk\Revit 2020\RevitAPI.dll</HintPath> <Private>False</Private> </Reference> <Reference Include="RevitAPIUI"> <HintPath>C:\Program Files\Autodesk\Revit 2020\RevitAPIUI.dll</HintPath> <Private>False</Private> </Reference>HintPath写死的是你本机的Revit安装路径,不同机器装的盘符不同,这里极容易编译失败。Private设为False表示不把RevitAPI拷贝到输出目录,因为运行期Revit自己会加载这些程序集,拷出去反而可能出现多份程序集导致的类型冲突。如果源码给的HintPath已经指向某个特定路径,你先看本机有没有,没有就改成你自己的实际路径。
这里分享一个我自己常用的替代方案:不直接改csproj里的HintPath,而是在项目里建一个RevitDll文件夹,把RevitAPI.dll和RevitAPIUI.dll复制进去,引用指到相对路径..\RevitDll\RevitAPI.dll。好处是换电脑、换Revit版本时只要替换这个文件夹里的文件,不用重新改工程配置。前提是这两份DLL只能本机使用,别想着再分发。
3.3 附加调试:让VS把断点打进Revit进程
编译完成后按F5能不能直接跑起来?默认不行,因为你的启动项目是一个类库,没有exe入口。你需要告诉Visual Studio"启动时帮我拉起Revit 2020"。在项目属性→调试选项卡里,选"启动外部程序",填C:\Program Files\Autodesk\Revit 2020\Revit.exe,命令行参数可以不填。这个配置其实写在.csproj.user文件里,直接改文件也行:
<Project> <PropertyGroup> <StartProgram>C:\Program Files\Autodesk\Revit 2020\Revit.exe</StartProgram> <StartArguments></StartArguments> <StartAction>Program</StartAction> </PropertyGroup> </Project>保存后F5,Visual Studio会先编译,然后启动Revit,并自动把调试器附加到Revit进程上。这时打开Developer选项卡里的命令,点一下就会命中你在源码里下的断点。注意一个细节:附加调试器的Revit实例会变得明显卡顿,这是.NET调试器跟踪托管代码的正常代价,不是插件死循环。RevitLookup源码本身就是一套很好的调试目标——你可以在Snoop命令的Execute方法第一行打断点,然后去点一个构件,逐步走一遍它收集对象的完整路径。
如果不想每次F5都启动全新Revit,也可以手动打开Revit和你的项目,再用VS的"调试→附加到进程"选中Revit.exe。我更喜欢这种模式,因为项目改完只要重新编译生成DLL,再重启Revit就能看效果,不需要让VS接管整个Revit生命周期。
4. RevitLookup 2020避坑排查:addin不生效、Snoop没反应、加载就闪退
这一章是血泪经验汇总。RevitLookup本身是一个成熟工具,但"成熟"指的是它的逻辑,不是你的电脑环境。下面几条是群里和论坛里反馈最集中、我自己也翻过车的场景,全部按现象→原因→解决来写。
4.1 现象:Developer选项卡打开了,菜单里却没有RevitLookup
addin文件放对了,Revit也重启了,但"附加模块"里空空如也。
原因按概率排序:第一,addin文件名或扩展名不对,放进去的是RevitLookup.addin.txt;第二,XML内容里Assembly路径指向的DLL不存在,Revit启动时扫码发现目标缺失就跳过了;第三,ClientId和已有插件重复,被Revit当成同一插件去重。
解决:先确认文件扩展名是.addin而不是.txt,在资源管理器里把"查看文件扩展名"打开看一眼;再用记事本打开addin,检查Assembly路径的盘符和目录是否真实存在,并确认目标DLL文件名和编译输出一致;最后用PowerShell跑一句[guid]::NewGuid().ToString()生成全新GUID替换ClientId,以后新插件都用这个办法生成,别手抄网上复制的GUID。
4.2 现象:addin路径正确,但Revit 2020启动时弹"插件加载错误"
这个比静默失败好一点,至少给了弹窗。弹窗内容一般会提示加载哪个程序集失败。
原因多数是DLL引用了Revit 2020不存在的程序集,或者编译目标是x86。RevitLookup源码默认是x64,但如果你在旧电脑上拿到的源码被前人改过平台目标,编译出来的DLL一进Revit进程就会因为位数不匹配被拒。
解决:用CorFlags.exe或者直接在VS里打开项目属性→生成→平台目标,确认是x64;然后在项目引用列表里删掉所有带黄色感叹号的项目,重新添加RevitAPI和RevitAPIUI,再编译一次。黄感叹号代表引用找不到目标文件,删掉重加比手动改HintPath更快。
4.3 现象:Snoop命令能点,但弹出的窗口里只显示一行"Object"
这种情况常发生在Snoop某个构件时,窗口打开却看不到期望的参数列表,只返回一个简单的Object节点,展开也什么都没有。
原因一般是Snoop的对象不是Element,而是ElementId或某个内嵌对象,RevitLookup没法从ElementId反查到可用元素——它需要你先真正选中元素才能拿到完整对象。另一个常见原因是你用了Snoop Pick但点到的是视图像素而不是构件图元。
解决:改回Snoop Current Selection,先在视图里框选或Tab键切换选中你要查的墙、门、族实例,确认状态栏出现图元高亮后再执行命令;如果还是空,检查你的选择集里是否混入了Group或ImportedInstance这类复合对象,先点选单个族实例再试。
4.4 现象:Snoop《某属性》时Revit直接崩溃,连错误日志都没有
这是最让人血压高的一条。通常发生在展开GeometryElement下的Solid、Face或Edge节点时,尤其是复杂体量、链接模型里的构件。
原因:RevitLookup在读取某些几何数据时调用的是API的get_Geometry()或ComputeDerivedGeometry,链接模型或未加载的图元会触发Revit的内部几何计算异常,进程直接挂掉。这不是你代码的问题,是Revit几何引擎的脾性。
解决:习惯性先"隔离变量"——Snoop同一个构件时,先展开Parameters确认数据能读,再展开Geometry,如果复现崩溃,换一个简单构件(比如普通墙体)再试,确认是构件本体的几何特殊性导致;让Revit先"完全加载"链接模型再Snoop;实在要查复杂几何,先配合源码在SnoopGeometry相关方法里临时包一层try-catch把异常文本写进日志文件,不用弹窗——这一步需要你会改源码,正好用得上项目里自带的那份代码。
4.5 现象:自己编译的DLL在别的机器上加载失败
你在A机器上编译通过,把RevitLookup.dll和addin复制到B机器,B机器Revit 2020启动后什么反应都没有。
原因:B机器可能没装对应的.NET Framework运行时;或者你的编译环境安装了某个高版本.NET库,DLL依赖了B机器没有的程序集;还有可能是Assembly绝对路径失效——你写的是C:\Users\A机器用户名\...,B机器根本没有这个目录。
解决:发布时带上编译用的目标框架安装包;用Dependencies工具查看DLL的依赖列表,把非系统程序集一并拷走;addin里Assembly路径保持相对路径风格。RevitLookup官方发布的addin模板里路径也都是绝对路径,但这属于"能跑但不好搬"的做法,真正跨机器分发给同事时,请先老老实实改成通用路径并测试一遍。
5. 把RevitLookup改造成自己的:新增菜单项和自定义Snoop逻辑
5.1 读懂入口:ExternalApplication的OnStartup里做了什么
源码在手,值得先读一遍入口类。App类继承IExternalApplication,在OnStartup里拿到UIControlledApplication,然后调用RibbonPanel相关方法创建按钮,把几个Snoop命令绑到PushButtonData上。整个注册菜单的过程不超过30行代码,逻辑很直白:创建面板→为每个命令创建按钮→绑定命令类。
读懂这段有一个直接收益:你可以把它的面板从附加模块移到自己项目的专属选项卡里,或者给按钮换图标。做法是改OnStartup里的CreateRibbonTab,把application.CreateRibbonTab("MyTools")加在创建面板之前,再让面板归属到这个选项卡。这样打开Revit,你自己的工具和原版Snoop并列,团队里分发时也比埋在"附加模块"里显眼得多。
5.2 加一个自定义命令:Snoop当前视图的所有选中族实例
RevitLookup原版只Snoop当前选择、DB和Pick对象,但它没有"一次性把当前视图所有图元列出来"的入口。当你处理大模型、想确认某面墙上到底挂了多少实例时,原版会让你一个个点。这个需求完全可以在源码里扩展——新增一个IExternalCommand:
[Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.ReadOnly)] public class SnoopActiveViewInstances : IExternalCommand { public Result Execute( ExternalCommandData commandData, ref string message, ElementSet elements) { var doc = commandData.Application.ActiveUIDocument.Document; var view = commandData.Application.ActiveUIDocument.ActiveGraphicalView; var collector = new FilteredElementCollector(doc, view.Id); var instances = collector .OfClass(typeof(FamilyInstance)) .Cast<FamilyInstance>() .ToList(); using (var form = new SnoopForms.SnoopForm(instances)) { form.ShowDialog(); } return Result.Succeeded; } }这段代码做了三件事:用FilteredElementCollector传入视图Id,限定只收集当前可见视图里的元素;再通过OfClass(typeof(FamilyInstance))过滤族实例;最后把它们整体塞进Snoop窗口,让用户按常规方式展开查看。TransactionMode.ReadOnly声明这个命令不修改文档,Revit会允许它在只读状态下快速执行。不要小看这一小段,它展示了Revit二次开发里最常用的收集器写法:先限定范围、再过滤类型、最后转成可操作列表。你以后写任何批量处理工具,骨架都是一模一样的。
新增命令之后还要在addin里补一段注册,让它出现在菜单里:
<AddIn Type="Command"> <Name>RevitLookup.SnoopActiveView</Name> <Assembly>C:\LookupOutput\RevitLookup.dll</Assembly> <FullClassName>RevitLookup.SnoopActiveViewInstances</FullClassName> <ClientId>D7F7D7A0-0000-4000-8000-ABCDEF123456</ClientId> <VendorId>ADSK</VendorId> </AddIn>注意这里Type从Application变成了Command,因为这段命令直接挂在外部工具按钮下,不依赖外部应用启动。一个addin文件里可以同时包含多个AddIn节点,你之前的Application节点别删,两者共存。重新编译后重启Revit,新命令会出现在外部工具列表里,点一下就能看到当前视图所有族实例的集合。
5.3 给Snoop窗口加一个"归属类型"列
如果你经常在大量构件间跳转,会发现原版Snoop窗口里对象列表没有显示每个元素的Category和Name,翻树查起来效率低。这个也可以在源码里改:SnoopForm的数据源绑定处,给每个节点加两个自定义属性,展示Element.GetType().Name和Category?.Name。改完编译,窗口列表会直观很多。这一步不是必须的,但做过一次你就明白:拿着源码改工具,比在外围用AutoCAD式的命令行取巧要可靠得多——因为你动的是数据源本身。
6. 让Lookup替你验收代码:参数写入前后对比的三步验收法
工具不只是用来查"这是什么",还能反过来验证你写的代码到底有没有生效。我这两年养成的一个习惯是:写任何会修改元素参数的插件时,先用RevitLookup把"手术前"的状态记录下来,跑完代码再Snoop同一根元素,对比参数值的变化。流程很短:先在视图里选中目标构件,Snoop Current Selection,找到你要改的参数名,记下旧值;然后运行你的外部命令;命令跑完后,不关闭Revit,重新选同一根构件,再次Snoop,看参数值是否按预期改变。如果值变了但类型不对,比如本该写double却写成了int,Snoop窗口的参数类型列会直接暴露类型不匹配。
这个方法比在代码里加Debug.Assert更贴近真实运行结果,因为它验证的是Revit数据库落库后的最终状态。我还经常在Snoop里展开Geometry节点,核对写完参数后几何是否跟着更新——比如改了一个族实例的Offset,Location点和Solid的Transform应当同步变化,如果几何没有跟随,说明你的参数改动没有触发Regenerate,这是Revit API里非常隐蔽的"静默失败",光靠看返回值发现不了。这种时候,我会在Execute方法末尾先调一次doc.Regenerate(),再重新Snoop对比。
需要注意,Snoop窗口展示的是内存态数据,如果命令在一个事务里改了值又回滚了,Snoop看到的仍然是原值,这是正常现象——不要误判为代码没执行。
最后分享一个我踩过的坑:早期我用Snoop检查某构件参数,发现值没变就认定是插件逻辑错了,调了半天才发现Snoop出来的是Parameter的AsValueString()格式,而这个格式是带单位后缀的,"2500"显示成"2500.0 mm",我呢,拿字符串做了精确匹配,当然一直失败。从那以后我养成了一个习惯:看参数值时先区分StorageType是Integer、Double还是String,再用对应的AsInteger()、AsDouble()或AsString()去读。这个习惯被我从RevitLookup带到了所有Revit二开项目里,希望你也能顺手用上。
本文还有配套的精品资源,点击获取