Navisworks API调用CurrentSelection.CopyFrom时出现空引用异常
问题背景
开发功能目标:检查Autodesk Navisworks当前选中元素是否属于特定碰撞测试结果,并选中所有包含任一选中元素的碰撞结果关联模型项。其中碰撞结果与模型项交集的代码运行正常,但调用Navisworks API执行选中操作时,在_doc.CurrentSelection.CopyFrom(selection)行触发空引用异常,异常来自NativeHandle.cs的RuntimeChecks方法,且该行涉及的元素均不为空。
正常运行的交集代码
internal List<ModelItem> GroupIntersectingClashResults(Guid clashTestId, NW.ModelItemCollection clashResultSelectionToCheck) { DocumentClash clashData = doc.GetClash(); DocumentClashTests clashTests = clashData.TestsData; var targetTest = clashTests.Tests.FirstOrDefault(t => t.Guid == clashTestId) as ClashTest; if (targetTest == null) { Log.Warning("Clash test not found for id: {Id}", clashTestId); return []; } List<IClashResult> intersectingResults = FindIntersectingClashResults(targetTest, clashResultSelectionToCheck); if (!intersectingResults.Any()) { Log.Warning("No intersecting items found for clash test id: {Id}", clashTestId); return []; } var mic = new List<ModelItem>(); foreach (IClashResult clashResult in intersectingResults) { mic.AddRange(clashResult.Selection1); mic.AddRange(clashResult.Selection2); } return mic; } private static List<IClashResult> FindIntersectingClashResults(ClashTest clashTest, NW.ModelItemCollection selectionToCheck) { var intersectionResults = new List<IClashResult>(); foreach (SavedItem item in clashTest.Children) { if (item is not IClashResult clashResult) continue; if (selectionToCheck.IsAnyContained(clashResult.CompositeItemSelection1) || selectionToCheck.IsAnyContained(clashResult.CompositeItemSelection2)) intersectionResults.Add(clashResult); } return intersectionResults; }
触发异常的选中代码
public ClashResultsNavigatorControl() { InitializeComponent(); InitializeContextMenu(); _doc = NW.Application.ActiveDocument; _clashTestNames = new Dictionary<Guid, string>(); NW.Application.ActiveDocument.Models.CollectionChanged += OnDocumentChange; _doc.CurrentSelection.Changed += OnCurrentSelectionChange; Disposed += OnDispose; _doc.GetClash().TestsData.Changed += OnClashTestsDataChange; } private void OnCurrentSelectionChange(object sender, EventArgs e) { try { var clashResultUtils = new ClashResultUtils(_doc); if (_doc.CurrentSelection.SelectedItems.Count == 0) return; List<ModelItem> selection = clashResultUtils .GroupIntersectingClashResults(_selectedClashTest.Guid, _doc.CurrentSelection.SelectedItems); foreach (ModelItem modelItem in selection) { var a = _doc.Models.FindIdForModelItem(modelItem); } if (selection.Count == 0) return; _doc.CurrentSelection.Changed -= OnCurrentSelectionChange; _doc.CurrentSelection.CopyFrom(selection); } catch (Exception ex) { ExceptionHandler.Handle(ex); } finally { _doc.CurrentSelection.Changed -= OnCurrentSelectionChange; _doc.CurrentSelection.Changed += OnCurrentSelectionChange; } }
问题原因分析
- ModelItem有效性问题:从碰撞结果获取的
Selection1/Selection2可能包含已失效的ModelItem(如模型被修改、元素被移除)或CompositeItem,这类项无法直接用于选中操作。 - CopyFrom参数类型兼容问题:
CurrentSelection.CopyFrom对输入集合的内部类型有严格要求,直接传入List<ModelItem>可能导致Native层处理异常。 - 事件绑定逻辑冗余:finally块中重复解绑再绑定事件,可能导致事件状态异常,且未处理重复绑定的情况。
修复方案
1. 过滤并验证ModelItem有效性
在获取关联模型项后,先过滤无效项,并处理CompositeItem(拆解为可选中的子项):
修改GroupIntersectingClashResults中的集合填充逻辑:
foreach (IClashResult clashResult in intersectingResults) { AddValidModelItems(clashResult.Selection1, mic); AddValidModelItems(clashResult.Selection2, mic); } // 新增辅助方法:处理CompositeItem并过滤无效项 private void AddValidModelItems(ModelItemCollection items, List<ModelItem> targetList) { foreach (ModelItem item in items) { if (!item.IsValid) continue; if (item.IsComposite) { // 拆解CompositeItem到底层可选中的子项 foreach (var child in item.Children) { if (child.IsValid) targetList.Add(child); } } else { targetList.Add(item); } } }
2. 显式创建ModelItemCollection传递给CopyFrom
避免直接传入List<ModelItem>,显式创建Navisworks原生的ModelItemCollection:
修改OnCurrentSelectionChange中的选中逻辑:
try { var clashResultUtils = new ClashResultUtils(_doc); if (_doc.CurrentSelection.SelectedItems.Count == 0 || _selectedClashTest == null) return; List<ModelItem> selection = clashResultUtils .GroupIntersectingClashResults(_selectedClashTest.Guid, _doc.CurrentSelection.SelectedItems); var validItems = selection.Where(item => item.IsValid).ToList(); if (validItems.Count == 0) return; // 先解绑事件,避免修改选中时递归触发 _doc.CurrentSelection.Changed -= OnCurrentSelectionChange; // 显式创建ModelItemCollection var targetSelection = new ModelItemCollection(); foreach (var item in validItems) { targetSelection.Add(item); } _doc.CurrentSelection.CopyFrom(targetSelection); } catch (Exception ex) { ExceptionHandler.Handle(ex); } finally { // 仅当事件未绑定时才重新绑定,避免重复绑定 if (!_doc.CurrentSelection.Changed.GetInvocationList().Contains(OnCurrentSelectionChange)) { _doc.CurrentSelection.Changed += OnCurrentSelectionChange; } }
3. 增加SelectedClashTest有效性检查
在执行交集查询前,先验证_selectedClashTest是否有效:
if (_selectedClashTest == null) { Log.Warning("No valid clash test selected"); return; }
内容的提问来源于stack exchange,提问作者Rade Tomovic
相关产品推荐
相关产品推荐

