You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
    }
}

问题原因分析

  1. ModelItem有效性问题:从碰撞结果获取的Selection1/Selection2可能包含已失效的ModelItem(如模型被修改、元素被移除)或CompositeItem,这类项无法直接用于选中操作。
  2. CopyFrom参数类型兼容问题:CurrentSelection.CopyFrom对输入集合的内部类型有严格要求,直接传入List<ModelItem>可能导致Native层处理异常。
  3. 事件绑定逻辑冗余: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 03:50:10