Revit中IUpdater引发同步警告问题求助:是否未正确注销?
问题背景
开发了一款基于IUpdater接口的Revit插件,启动时注册UpdaterFloorsROofsWalls,关闭时尝试注销,但未安装插件的机器同步项目时出现警告:
you are trying to synchronize was modified by the third-party updater Unknown : Element Updater which is not currently installed.
(您尝试同步的内容已被未安装的第三方更新器Unknown:Element Updater修改)
需确认问题是否由未正确注销IUpdater引发,并找到解决方法。
插件关键代码
启动时注册Updater
public Result OnStartup(UIControlledApplication a) { UpdaterFloorsROofsWalls elementUpdater = new UpdaterFloorsROofsWalls(a.ActiveAddInId); UpdaterRegistry.RegisterUpdater(elementUpdater); return Result.Succeeded; }
关闭时注销Updater
public Result OnShutdown(UIControlledApplication a) { // IUpdater is required to be Unregistered on shut down UpdaterFloorsROofsWalls elementUpdater = new UpdaterFloorsROofsWalls(a.ActiveAddInId); UpdaterRegistry.UnregisterUpdater(elementUpdater.GetUpdaterId()); return Result.Succeeded; }
IUpdater类定义
using Autodesk.Revit.DB; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace WarningViewer { internal class UpdaterFloorsROofsWalls : IUpdater { static AddInId m_appId; static UpdaterId m_updaterId; public UpdaterFloorsROofsWalls (AddInId id){ m_appId = id; m_updaterId = new UpdaterId(m_appId, new Guid("FBFBF6B2-4C06-42d4-97C1-D1B4EB593EFF")); } public void Execute(UpdaterData data) { try { Document doc = data.GetDocument(); if (doc.ActiveView.Name == _Utilities.viewName) { List<ElementId> failingElements = _Utilities.GetElementIdsOfFailingElements(doc); _Utilities.ShowAllWarningsIn3dSub(failingElements, doc, doc.ActiveView); } } catch (Exception ex) { MessageBox.Show(ex.Message); } } public string GetAdditionalInformation() { return "Updater for Floors Roofs and Walls"; } public ChangePriority GetChangePriority() { return ChangePriority.FloorsRoofsStructuralWalls; } public UpdaterId GetUpdaterId() { return m_updaterId; } public string GetUpdaterName() { return "Element Updater"; } } }
问题原因确认
1. 注销逻辑的潜在问题
你的注销代码中,m_appId和m_updaterId是静态变量,虽然当前启动/关闭各实例化一次不会直接报错,但静态变量会导致多次实例化时覆盖原有值,存在潜在风险。不过这不是同步警告的直接原因。
2. 核心原因:模型中留存了Updater修改记录
Revit会在模型元素被IUpdater修改后,将该Updater的ID和名称记录在模型中。即使你在插件关闭时注销了Updater,已修改元素的记录依然存在。当未安装插件的机器同步项目时,Revit找不到对应的Updater,就会弹出警告。
另外,观察你的Execute方法,如果_Utilities.ShowAllWarningsIn3dSub只是显示警告、未修改模型,那你误用了IUpdater——IUpdater的设计目的是响应模型变更并修改模型,而非仅做UI展示,这种场景应该用DocumentChanged事件替代,避免在模型中留下Updater痕迹。
解决方法
方法一:修正插件的Updater实现与注销逻辑
- 将静态变量改为实例变量,避免静态共享带来的意外问题:
// 替换原静态变量 private readonly AddInId m_appId; private readonly UpdaterId m_updaterId; public UpdaterFloorsROofsWalls(AddInId id) { m_appId = id; m_updaterId = new UpdaterId(m_appId, new Guid("FBFBF6B2-4C06-42d4-97C1-D1B4EB593EFF")); }
- 简化注销逻辑,无需新建Updater实例,直接构造UpdaterId注销:
public Result OnShutdown(UIControlledApplication a) { var updaterId = new UpdaterId(a.ActiveAddInId, new Guid("FBFBF6B2-4C06-42d4-97C1-D1B4EB593EFF")); UpdaterRegistry.UnregisterUpdater(updaterId); return Result.Succeeded; }
方法二:清理模型中的Updater痕迹
如果模型中已经留存了该Updater的记录,可编写临时工具清理:
// 在文档可用的上下文(如外部命令)中执行 public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) { var doc = commandData.Application.ActiveUIDocument.Document; var updaterId = new UpdaterId(commandData.Application.ActiveAddInId, new Guid("FBFBF6B2-4C06-42d4-97C1-D1B4EB593EFF")); using(var trans = new Transaction(doc, "Remove Updater Info")) { trans.Start(); UpdaterRegistry.RemoveAllUpdaterInfoFromDocument(doc, updaterId); trans.Commit(); } return Result.Succeeded; }
运行此工具后,模型中该Updater的记录会被移除,未装插件的机器同步时就不会再弹出警告。
方法三:替换IUpdater为事件监听(仅UI展示场景)
如果你的插件只是显示警告、无需修改模型,完全不需要使用IUpdater,改用DocumentChanged事件监听模型变更,再执行UI展示逻辑:
public Result OnStartup(UIControlledApplication a) { a.ControlledApplication.DocumentChanged += OnDocumentChanged; return Result.Succeeded; } public Result OnShutdown(UIControlledApplication a) { a.ControlledApplication.DocumentChanged -= OnDocumentChanged; return Result.Succeeded; } private void OnDocumentChanged(object sender, DocumentChangedEventArgs e) { var doc = e.GetDocument(); if (doc.ActiveView.Name == _Utilities.viewName) { List<ElementId> failingElements = _Utilities.GetElementIdsOfFailingElements(doc); _Utilities.ShowAllWarningsIn3dSub(failingElements, doc, doc.ActiveView); } }
这种方式不会在模型中留下任何第三方工具的记录,从根源避免同步警告。
内容的提问来源于stack exchange,提问作者Skinner

