# Navisworks 2017 API 使用指南 ## 概述 本指南基于实际项目开发经验,汇总了Navisworks 2017 API的核心用法和最佳实践。重点覆盖COM API的属性操作功能,为插件开发提供实用的参考。 ## 目录 1. [环境配置](#环境配置) 2. [COM API 基础](#com-api-基础) 3. [属性操作 API](#属性操作-api) 4. [常见问题与解决方案](#常见问题与解决方案) 5. [最佳实践](#最佳实践) 6. [参考资源](#参考资源) ## 环境配置 ### 必需引用 ```xml ``` ### 命名空间导入 ```csharp using Autodesk.Navisworks.Api; using Autodesk.Navisworks.Api.Plugins; using ComApi = Autodesk.Navisworks.Api.Interop.ComApi; using ComApiBridge = Autodesk.Navisworks.Api.ComApi.ComApiBridge; ``` ## COM API 基础 ### COM API 状态对象获取 ```csharp // 获取COM API状态对象 - 这是所有COM操作的入口点 ComApi.InwOpState10 state = ComApiBridge.State; ``` ### .NET 对象与 COM 对象转换 ```csharp // ModelItem 转换为 COM Path ComApi.InwOaPath3 comPath = (ComApi.InwOaPath3)ComApiBridge.ToInwOaPath(modelItem); // ModelItemCollection 转换为 COM Selection ComApi.InwOpSelection comSelection = ComApiBridge.ToInwOpSelection(modelItems); ``` ### 属性节点获取 ```csharp // 获取模型项的属性节点 ComApi.InwGUIPropertyNode2 propertyNode = (ComApi.InwGUIPropertyNode2)state.GetGUIPropertyNode(comPath, false); ``` ## 属性操作 API ### 1. 创建用户定义属性 #### 基本创建流程 ```csharp public static void CreateUserDefinedProperty(ModelItem item, string categoryName, Dictionary properties) { // 1. 获取COM API状态对象 ComApi.InwOpState10 state = ComApiBridge.State; // 2. 转换ModelItem为COM路径 ComApi.InwOaPath3 path = (ComApi.InwOaPath3)ComApiBridge.ToInwOaPath(item); // 3. 获取属性节点 ComApi.InwGUIPropertyNode2 propertyNode = (ComApi.InwGUIPropertyNode2)state.GetGUIPropertyNode(path, false); // 4. 创建属性类别容器 ComApi.InwOaPropertyVec propertyCategory = (ComApi.InwOaPropertyVec)state.ObjectFactory( ComApi.nwEObjectType.eObjectType_nwOaPropertyVec, null, null); // 5. 添加具体属性 foreach (var kvp in properties) { AddProperty(state, propertyCategory, kvp.Key, kvp.Value, kvp.Key + "_Internal"); } // 6. 设置用户定义属性 (index=0表示创建新类别) propertyNode.SetUserDefined(0, categoryName, categoryName + "_Internal", propertyCategory); } // 辅助方法:添加单个属性到类别 private static void AddProperty(ComApi.InwOpState10 state, ComApi.InwOaPropertyVec propertyCategory, string displayName, string value, string internalName) { ComApi.InwOaProperty property = (ComApi.InwOaProperty)state.ObjectFactory( ComApi.nwEObjectType.eObjectType_nwOaProperty, null, null); property.name = internalName; // 内部名称 property.UserName = displayName; // 显示名称 property.value = value; // 属性值 propertyCategory.Properties().Add(property); } ``` #### 关键参数说明 - **index=0**: 创建新的用户定义属性类别 - **categoryName**: 属性类别的显示名称 - **internalName**: 属性类别的内部名称(建议添加"_Internal"后缀) ### 2. 修改用户定义属性 #### 查找现有属性索引 ```csharp private static int FindUserDefinedPropertyIndex(ComApi.InwGUIPropertyNode2 propertyNode, string categoryName) { int index = 0; foreach (ComApi.InwGUIAttribute2 attribute in propertyNode.GUIAttributes()) { if (attribute.UserDefined && attribute.ClassUserName == categoryName) { return index; } index++; } return -1; // 未找到 } ``` #### 修改现有属性 ```csharp public static void UpdateUserDefinedProperty(ModelItem item, string categoryName, Dictionary newProperties) { ComApi.InwOpState10 state = ComApiBridge.State; ComApi.InwOaPath3 path = (ComApi.InwOaPath3)ComApiBridge.ToInwOaPath(item); ComApi.InwGUIPropertyNode2 propertyNode = (ComApi.InwGUIPropertyNode2)state.GetGUIPropertyNode(path, false); // 查找现有属性类别的索引 int existingIndex = FindUserDefinedPropertyIndex(propertyNode, categoryName); if (existingIndex >= 0) { // 创建新的属性集合 ComApi.InwOaPropertyVec newPropertyCategory = (ComApi.InwOaPropertyVec)state.ObjectFactory( ComApi.nwEObjectType.eObjectType_nwOaPropertyVec, null, null); // 添加新的属性值 foreach (var kvp in newProperties) { AddProperty(state, newPropertyCategory, kvp.Key, kvp.Value, kvp.Key + "_Internal"); } // 使用现有索引覆盖属性类别 propertyNode.SetUserDefined(existingIndex, categoryName, categoryName + "_Internal", newPropertyCategory); } else { // 如果不存在,则创建新的 CreateUserDefinedProperty(item, categoryName, newProperties); } } ``` #### 关键要点 - **使用现有索引**: 修改时必须使用找到的现有索引,而不是0 - **完全覆盖**: `SetUserDefined`会完全替换现有属性,而不是增量更新 ### 3. 删除用户定义属性 #### 计算相对索引 ```csharp private static int GetUserDefinedPropertyRelativeIndex(ComApi.InwGUIPropertyNode2 propertyNode, string categoryName) { int relativeIndex = 0; foreach (ComApi.InwGUIAttribute2 attribute in propertyNode.GUIAttributes()) { if (attribute.UserDefined) { if (attribute.ClassUserName == categoryName) { return relativeIndex; } relativeIndex++; } } return -1; // 未找到 } ``` #### 删除属性类别 ```csharp public static void RemoveUserDefinedProperty(ModelItem item, string categoryName) { ComApi.InwOpState10 state = ComApiBridge.State; ComApi.InwOaPath3 path = (ComApi.InwOaPath3)ComApiBridge.ToInwOaPath(item); ComApi.InwGUIPropertyNode2 propertyNode = (ComApi.InwGUIPropertyNode2)state.GetGUIPropertyNode(path, false); // 计算用户定义属性的相对索引 int relativeIndex = GetUserDefinedPropertyRelativeIndex(propertyNode, categoryName); if (relativeIndex >= 0) { // 用户定义属性索引从1开始,所以需要+1 int userDefinedIndex = relativeIndex + 1; // 使用RemoveUserDefined方法完全删除属性类别 propertyNode.RemoveUserDefined(userDefinedIndex); } } ``` #### 关键要点 - **索引从1开始**: 用户定义属性的索引从1开始,不是0 - **相对索引**: 需要计算在用户定义属性中的相对位置 - **完全删除**: `RemoveUserDefined`会完全移除属性类别,不留痕迹 ### 4. 读取属性值 #### 使用COM API读取 ```csharp public static string GetPropertyValueViaCom(ModelItem item, string categoryName, string propertyName) { try { ComApi.InwOpState10 state = ComApiBridge.State; ComApi.InwOaPath3 path = (ComApi.InwOaPath3)ComApiBridge.ToInwOaPath(item); ComApi.InwGUIPropertyNode2 propertyNode = (ComApi.InwGUIPropertyNode2)state.GetGUIPropertyNode(path, false); // 遍历属性类别 foreach (ComApi.InwGUIAttribute2 attribute in propertyNode.GUIAttributes()) { if (attribute.UserDefined && attribute.ClassUserName == categoryName) { // 遍历类别中的属性 foreach (ComApi.InwOaProperty property in attribute.Properties()) { if (property.UserName == propertyName) { return property.value?.ToString() ?? ""; } } } } return ""; // 未找到 } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"读取属性失败: {ex.Message}"); return ""; } } ``` ## 常见问题与解决方案 ### 1. 属性缓存同步问题 **问题**: COM API写入属性后,.NET API读取到的仍是旧值。 **解决方案**: ```csharp // 强制刷新文档状态 private static void ForceDocumentRefresh() { try { var doc = Application.ActiveDocument; if (doc != null) { // 强制刷新文档状态 doc.CurrentSelection.Clear(); Application.DoEvents(); } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"文档刷新失败: {ex.Message}"); } } ``` ### 2. 索引计算错误 **问题**: 使用错误的索引导致操作失败。 **解决方案**: - 创建新属性:使用 `index = 0` - 修改现有属性:使用绝对索引(从0开始计数所有属性) - 删除属性:使用相对索引+1(在用户定义属性中的位置+1) ### 3. 重复属性类别 **问题**: 每次操作都创建新的属性类别。 **解决方案**: ```csharp // 始终先检查是否存在现有属性 int existingIndex = FindUserDefinedPropertyIndex(propertyNode, categoryName); if (existingIndex >= 0) { // 修改现有属性 propertyNode.SetUserDefined(existingIndex, categoryName, internalName, newProperties); } else { // 创建新属性 propertyNode.SetUserDefined(0, categoryName, internalName, newProperties); } ``` ## 最佳实践 ### 1. 错误处理 ```csharp try { // COM API 操作 } catch (System.Runtime.InteropServices.COMException comEx) { // 处理COM特定错误 LogManager.WriteLog($"COM API错误: {comEx.Message}"); } catch (Exception ex) { // 处理一般错误 LogManager.WriteLog($"操作失败: {ex.Message}"); } ``` ### 2. 日志记录 ```csharp // 详细的操作日志 LogManager.WriteLog($"[属性操作] 开始为 {items.Count} 个模型设置属性"); LogManager.WriteLog($"[属性操作] 类别名称: {categoryName}"); LogManager.WriteLog($"[属性操作] 操作完成,成功: {successCount}, 失败: {failCount}"); ``` ### 3. 批量操作 ```csharp public static int BatchUpdateProperties(ModelItemCollection items, string categoryName, Dictionary properties) { int successCount = 0; foreach (ModelItem item in items) { try { UpdateUserDefinedProperty(item, categoryName, properties); successCount++; } catch (Exception ex) { LogManager.WriteLog($"处理模型 {item.DisplayName} 时失败: {ex.Message}"); } } return successCount; } ``` ### 4. 内存管理 ```csharp // COM对象使用完毕后释放引用 ComApi.InwOaPropertyVec propertyCategory = null; ComApi.InwOaProperty property = null; try { // 使用COM对象 } finally { // 释放COM对象引用 if (propertyCategory != null) { System.Runtime.InteropServices.Marshal.ReleaseComObject(propertyCategory); } if (property != null) { System.Runtime.InteropServices.Marshal.ReleaseComObject(property); } } ``` ## API 索引机制总结 | 操作类型 | 索引类型 | 索引值 | 说明 | |---------|---------|--------|------| | 创建新属性 | 固定值 | 0 | 始终使用0创建新的用户定义属性 | | 修改现有属性 | 绝对索引 | 0-n | 在所有属性中的位置(包括系统属性) | | 删除属性 | 相对索引+1 | 1-n | 在用户定义属性中的位置+1 | ## 参考资源 ### 官方文档 - Navisworks 2017 SDK 文档(安装目录下的api/documentation文件夹) - Autodesk Developer Network (ADN) ### 社区资源 - [Autodesk论坛 - Navisworks API](https://forums.autodesk.com/t5/navisworks-api/bd-p/95) - [TwentyTwo博客 - Navisworks API教程](https://twentytwo.space/) ### 实用工具 - Navisworks Object Model Browser(查看对象结构) - Visual Studio 调试工具(COM对象检查) --- ## 更新日志 ### v1.0 (2025-06-22) - 初始版本,基于物流属性管理功能开发经验 - 涵盖COM API属性操作的核心方法 - 包含实际项目中遇到的问题和解决方案 --- **注意**: 本指南将持续更新,补充更多API用法和最佳实践。欢迎贡献新的内容和改进建议。