资讯详情

SolidWorks二次开发:批量读取自定义属性的GetAll3方法实战

📅 2026/10/1 2:24:08 | 华诺云谱 👁 阅读
SolidWorks二次开发:批量读取自定义属性的GetAll3方法实战
刚接手一个SolidWorks二次开发项目时十个需求里有八个绕不开自定义属性。工程图标题栏要自动填图号、名称、材料BOM要导出的毛重净重呆在属性里PDM要按设计者、审核日期做检索甚至装配体里每个零件的物料编码也都挂在属性上。你不想让用户一个一个打开模型去抄那就得用代码把这些属性一次性拉出来。CustomPropertyManager就是干这个的而它里面的GetAll3方法是我在C#环境里最常用、也最顺手的一个接口。这篇文章就把这个项目里积累的经验完整拆开从方法签名、参数含义到C#实现代码和踩过的坑一次性说完。适合正在做SolidWorks工具开发、需要批量读取模型属性的朋友参考。1. 项目综述为什么读属性这件事值得专门做一次开发1.1 自定义属性在SolidWorks里的真实地位很多刚接触SolidWorks二次开发的人会低估自定义属性的价值总觉得“不就是几个键值对吗”。但在真实的生产环境里自定义属性是连接三维模型和工程业务系统的桥梁。工程图模板里的$PRP:图号、$PRP:材料本质上就是在读模型的自定义属性BOM表里要显示的“供应商代码”也一样来自属性。SolidWorks自带的任务窗格能让你手动填属性但当你面对的是几百个零件、需要从ERP系统同步数据过来、或者要在出图前批量检查属性是否填写完整时手动操作就完全不现实了。这个项目的核心需求就是在不打开SolidWorks界面的前提下或者最少程度地打开用C#写一个独立程序遍历指定目录下的所有零件和装配体把每个文件的自定义属性全部读出来输出成结构化数据供后续逻辑使用。GetAll3就是整个链条里最关键的一环——它负责把某个文档或某个配置下所有自定义属性一口气拿出来包括属性名、原始值、数据类型以及表达式解析后的实际值。1.2 适合谁来参考、解决什么问题这篇文章的内容适合两类人。一类是刚开始接触SolidWorks API的C#开发者想搞清楚CustomPropertyManager到底怎么用、GetAll3和GetAll、GetAll2之间有什么区别另一类是已经能跑通简单宏、但遇到批量读取、多配置、COM互操作问题时需要一份排错清单的工程师。读完你可以直接照着第四节代码写一个能用的批量属性导出工具也可以把第五节的问题手册当作排查参考。2. CustomPropertyManager核心解析管理器实例从哪来、配置名传什么2.1 获取管理器实例的几种正确姿势CustomPropertyManager是SolidWorks API里专门管理自定义属性的COM对象。它本身没有公开的构造函数只能通过文档对象获取。我把实际开发中验证过可行的三种方式列出来// 方式一直接从当前文档模型获取零件/装配体/工程图通用 CustomPropertyManager cpm swModel.CustomPropertyManager(); // 方式二从ModelDocExtension获取效果等价某些版本更推荐 CustomPropertyManager cpm2 swModel.Extension.CustomPropertyManager(); // 方式三从Configuration对象获取用于精确读取指定配置的属性 Configuration config swModel.GetActiveConfiguration(); CustomPropertyManager cpm3 config.CustomPropertyManager;方式一和方式二返回的对象是同一个东西区别在于方法所在的接口层级不同。方式三适合需要拿某个具体配置的属性值时使用因为配置对象本身就绑定了对应的配置名。注意如果传入的配置名在文档中不存在CustomPropertyManager方法不会帮你创建配置而是返回一个空的管理器某些版本会返回null后续调用GetAll3时count可能为0或直接抛异常。代码里要做空引用判断。2.2 配置名参数空字符串的真实含义CustomPropertyManager方法签名里有个BSTR类型的ConfigurationName参数。很多初学者会问传空字符串是不是就表示“当前激活配置”根据SolidWorks API文档和我的实测空字符串在不同版本、不同文档类型下表现并不完全一致。对于零件和装配体传空字符串通常可以获取到“默认配置”的自定义属性但对于设置了多个配置的文档如果你需要读取的是某个非默认配置的属性就必须显式传入配置名。举个例子// 假设模型有配置 默认 和 大直径 CustomPropertyManager defaultCpm swModel.CustomPropertyManager(默认); CustomPropertyManager variantCpm swModel.CustomPropertyManager(大直径);这个特性在读取“配置特定属性”时非常有用。SolidWorks的属性体系分为三种文档属性全配置共用、配置特定属性仅某个配置生效、以及切割清单属性焊件/钣金里常见。CustomPropertyManager配合具体配置名拿到的正是对应配置下的属性集合。如果你不关心配置区别只想拿到所有属性传空字符串加上遍历全部配置组合使用会更稳妥。2.3 管理器的其他常用方法概览GetAll3不是孤零零存在的方法CustomPropertyManager还有一整套配套接口。我快速过几个高频方法方便你做选型方法作用使用场景Add / Add3新增属性需要批量写属性时Get / Get2读取单个属性名对应的值只查两个已知属性时GetAll / GetAll2 / GetAll3一次性读取全部属性批量导出、全量比对时Delete删除指定属性清理冗余属性时GetCustomPropertyValue读取解析值老接口需要快速拿到表达式求值结果时实际开发里如果只是查一两个属性用Get方法就够了但只要涉及批量导出、属性完整性检查这类需求我会优先选GetAll3因为它在一次COM调用里返回全部数据效率远高于循环调用Get。3. GetAll3方法深度拆解五个输出参数到底分别说明什么3.1 方法签名与参数语义GetAll3的方法定义在SolidWorks API文档里长这样void GetAll3( out int Count, out Array Names, out Array Values, out Array Types, out Array ResolvedValues );五个参数全是输出参数这点和C#常见的返回单个值的习惯很不一样。Count是属性条目总数Names是属性名数组Values是属性原始值数组注意这个原始值指的是属性编辑框里保存的“公式原文”Types是属性类型枚举数组ResolvedValues是表达式求值后的实际值数组。我拿一个实际例子说明原始值和解析值的区别。假设模型里有个属性叫“总体积”它的原始值是SW-体积零件1.SLDPRT这样的表达式这是SolidWorks内部链接到质量属性的表达式。如果你拿Values数组去用拿到的是这串公式只有拿ResolvedValues数组才能得到真实的体积数值比如12345.678的字符串。这个区别在本文第五节还会重点展开。3.2 属性类型枚举swCustomPropertyType_e对照Types数组的元素是整数对应swCustomPropertyType_e枚举。我在项目里实际遇到过的类型有这些枚举名值说明swCustomPropertyUnknown0未知类型一般不会正常出现swCustomPropertyString1文本字符串最常见的类型swCustomPropertyNumber2数值注意存储仍然为字符串swCustomPropertyDate3日期swCustomPropertyYesNo4是/否布尔值swCustomPropertyList5下拉列表项这里有个容易踩的坑SolidWorks属性系统存储的“值”本质上全是字符串即使是数值类型Value数组里给你返回的也是3.14这种字符串而不是double。类型枚举只是告诉你这个属性在界面里是被当成数值还是文本对待的。如果你需要参与运算记得自己转换类型。3.3 Get3个版本的演化与取舍SolidWorks从很早就提供了GetAll后来逐步演进到GetAll2、GetAll3。对比一下这三个方法的输出差异方法CountNamesValuesTypesResolvedValuesGetAll有有有无无GetAll2有有有有无GetAll3有有有有有我的建议很直接如果你的目标平台是SolidWorks 2008之后的版本完全可以直接用GetAll3。一次调用能拿到的东西最全代码里多写两个数组的接收和判断成本很低但后续想用解析值做业务逻辑时就不用改代码返工了。如果考虑到老版本兼容可以写一个版本判断根据SolidWorks版本号决定调用哪个方法。4. C#实操全流程从打开模型到输出全部属性4.1 环境准备与必要的引用开发环境需要三样东西Visual Studio哪个版本都行我用的是2019/2022、SolidWorks安装目录下的Interop.SldWorks.dll位于api\redist文件夹以及以管理员身份运行的Windows环境如果你是64位系统注意生成目标的平台选择。新建一个控制台应用程序.NET Framework 4.8不要选.NET CoreSolidWorks COM互操作用Framework最省心添加引用using SolidWorks.Interop.sldworks; using SolidWorks.Interop.swconst;4.2 基础代码获取当前活动文档的全部属性这是最简版本适合在SolidWorks的宏环境里验证思路public void ReadActiveDocumentProperties() { ISldWorks swApp (ISldWorks)Marshal.GetActiveObject(SldWorks.Application); IModelDoc2 swModel (IModelDoc2)swApp.ActiveDoc; if (swModel null) { Console.WriteLine(当前没有打开的文档); return; } CustomPropertyManager cpm swModel.CustomPropertyManager(); if (cpm null) { Console.WriteLine(无法获取自定义属性管理器); return; } int count 0; object namesObj null; object valuesObj null; object typesObj null; object resolvedObj null; cpm.GetAll3(out count, out namesObj, out valuesObj, out typesObj, out resolvedObj); Console.WriteLine($共计 {count} 条属性); if (count 0) return; Array names namesObj as Array; Array values valuesObj as Array; Array types typesObj as Array; Array resolved resolvedObj as Array; for (int i 0; i count; i) { string name names.GetValue(i)?.ToString() ?? ; string value values.GetValue(i)?.ToString() ?? ; string resolvedValue resolved.GetValue(i)?.ToString() ?? ; int type Convert.ToInt32(types.GetValue(i)); Console.WriteLine(${name} {value} (原始), {resolvedValue} (解析)); } }代码里有个关键细节GetAll3的out参数在我实际使用中声明为object比声明为Array更稳妥。原因在于COM互操作时SafeArray可能会以VARIANT类型传递强声明为Array有时会产生类型不匹配异常。用object接收后再手动转成Array兼容性最好。4.3 脱离SolidWorks界面批量读取指定文件夹内的模型属性大多数真实项目都不希望用户手动打开每一个文件。我写了一个批量处理版本思路是先通过OpenDoc6以“不可见模式”打开模型读属性读完关闭。需要注意OpenDoc6打开文档时即便设置为不可见SolidWorks进程还是会被启动所以程序运行时能看到后台有个SW进程在运转。public void BatchReadProperties(string folderPath) { ISldWorks swApp (ISldWorks)Marshal.GetActiveObject(SldWorks.Application); string[] fileExtensions { .sldprt, .sldasm }; var files Directory.GetFiles(folderPath, *.*, SearchOption.AllDirectories) .Where(f fileExtensions.Contains(Path.GetExtension(f).ToLower())) .ToList(); foreach (string file in files) { int errors 0; int warnings 0; // 只读模式打开不显示窗口 IModelDoc2 swModel (IModelDoc2)swApp.OpenDoc6( file, (int)swDocumentTypes_e.swDocPART, (int)swOpenDocOptions_e.swOpenDocOptions_Silent, , ref errors, ref warnings); if (swModel null) { Console.WriteLine($打开失败: {file}); continue; } ReadAndPrintModelProperties(swModel, file); swApp.CloseDoc(swModel.GetTitle()); Marshal.ReleaseComObject(swModel); } }这里有个重要的性能细节批量打开几百分文件时OpenDoc6的Silent选项是必须的否则SolidWorks会一个个弹窗口速度慢到让人崩溃。同时关闭文档后记得用Marshal.ReleaseComObject释放COM引用否则进程里会积累大量托管引用最终导致SolidWorks进程卡死。4.4 将属性集合转换成业务友好的Dictionary结构GetAll3拿到的是四个平行数组用起来不够直观。我习惯再封装一层把属性数据转成Dictionarystring, PropertyItem方便业务代码按属性名直接查询。public class ModelProperty { public string Name { get; set; } public string Value { get; set; } public string ResolvedValue { get; set; } public int Type { get; set; } } public Dictionarystring, ModelProperty GetAllProperties(IModelDoc2 swModel) { var result new Dictionarystring, ModelProperty(StringComparer.OrdinalIgnoreCase); CustomPropertyManager cpm swModel.CustomPropertyManager(); if (cpm null) return result; int count 0; object namesObj null; object valuesObj null; object typesObj null; object resolvedObj null; cpm.GetAll3(out count, out namesObj, out valuesObj, out typesObj, out resolvedObj); Array names namesObj as Array; Array values valuesObj as Array; Array types typesObj as Array; Array resolved resolvedObj as Array; for (int i 0; i count; i) { string name names.GetValue(i)?.ToString() ?? ; if (string.IsNullOrEmpty(name)) continue; result[name] new ModelProperty { Name name, Value values?.GetValue(i)?.ToString() ?? , ResolvedValue resolved?.GetValue(i)?.ToString() ?? , Type Convert.ToInt32(types?.GetValue(i) ?? 0) }; } return result; }用Dictionary的好处非常明显。后续代码想判断“物料编码是否存在”不用再写一个遍历数组的循环直接dict.ContainsKey(物料编码)即可。而且测试中发现SolidWorks属性名对大小写不敏感界面里就默认不区分大小写所以我用了OrdinalIgnoreCase比较器避免大小写不一致导致查询不到。4.5 遍历所有配置获取配置特定属性如果你的模型是多配置的只读默认配置显然会漏数据。完整方案是先拿全部配置名再逐个配置获取属性public void ReadAllConfigurationProperties(IModelDoc2 swModel) { // 获取所有配置名称 ConfigurationManager configMgr swModel.ConfigurationManager; string[] configNames configMgr.GetConfigurationNames(); foreach (string configName in configNames) { CustomPropertyManager configCpm swModel.CustomPropertyManager(configName); if (configCpm null) continue; int count 0; object namesObj null; object valuesObj null; object typesObj null; object resolvedObj null; configCpm.GetAll3(out count, out namesObj, out valuesObj, out typesObj, out resolvedObj); Console.WriteLine($配置 [ {configName} ] 有 {count} 条属性); Array names namesObj as Array; Array values valuesObj as Array; for (int i 0; i count; i) { string name names.GetValue(i)?.ToString() ?? ; string value values.GetValue(i)?.ToString() ?? ; Console.WriteLine($ {name} {value}); } } }5. 常见问题与排查技巧实录我踩过的坑都在这里5.1 数组下标到底是从0开始还是从1开始这是个非常容易让人蒙圈的点。SolidWorks API文档里并没有明确写数组基索引更麻烦的是不同接口表现不一致。我的实测结论是GetAll3返回的SafeArray下标从0开始循环用for (int i 0; i count; i)即可正确遍历。但Get方法返回的单值数组、以及某些Old API的集合比如GetConfigurations确实存在从1开始的情况。如果你写的代码读取结果总是不对第一个怀疑的就是数组基索引。排查方法是在循环里打印第一条和最后一条数据肉眼确认哪条是真实的属性名。另外使用Array.GetValue(i)这个通用方法时即使原数组是0-based它也是按索引号访问不用关心上下界比直接用names[i]更安全。5.2 读出来的Value是一堆公式不是我要的数值这是GetAll3和GetAll2最大的差异点也是用不好GetAll3最常见的翻车现场。当你在SolidWorks界面里给属性填了SW-质量零件1.SLDPRT这类表达式时Values数组返回的是表达式原文ResolvedValues数组返回的是计算结果。很多人只取Values发现拿到的是一串类似SW-Mass...的字符串就以为API返回错了。解决办法就是使用ResolvedValues。但要注意ResolvedValues是字符串类型拿到12.345之后仍然要自己double.TryParse才能参与运算。另外如果表达式无法解析比如引用了已删除的尺寸ResolvedValues可能会是空字符串或者错误提示文本业务代码里要做容错处理。5.3 COM互操作出现Access Violation异常写C#调用SolidWorks API时偶尔会遇到Access ViolationC0000005异常。这个问题的成因很复杂但我遇到最多的两类情况是一是调用了已经被释放的COM对象比如文档关闭后还去访问它的CustomPropertyManager二是32位/64位不匹配比如SolidWorks是64位而你用AnyCPU编译生成进程可能在64位环境下加载失败。我的标准做法是所有生成的exe都设为x64平台如果你的SW是64位所有从API拿到的COM对象在用完后统一按逆序释放。ReleaseComObject不是必须的垃圾回收器最终会处理但在批量处理几百个文件时显式释放能让内存占用稳定在一个低位。5.4 返回的Array转成string[]失败有的机器上namesObj as string[]能够成功有些机器上却返回null。原因在于COM调用的底层封送机制会根据调用方式早期绑定还是晚期绑定产生不同的托管类型可能是object[]也可能是string[]。我建议永远先as Array再通过GetValue(i)获取元素不要强转成具体类型数组。// 稳定写法as Array GetValue Array names namesObj as Array; if (names null) return; string firstName names.GetValue(0) as string; // 不推荐直接强转string[] // string[] names (string[])namesObj; // 可能抛异常5.5 某些文档的CustomPropertyManager返回null如果你调用swModel.CustomPropertyManager()得到null先别急着怀疑自己代码。有几个已知原因文档还在加载中文档类型不支持例如某些临时文件或者传入的配置名在该文档中不存在。我处理的方式是先判断文档类型再判断配置名是否存在if (swModel.GetType() (int)swDocumentTypes_e.swDocDRAWING) { // 工程图的属性管理器和零件装配体一致可以直接用 } string[] configNames swModel.ConfigurationManager.GetConfigurationNames(); // 在configNames里确认传入的配置名确实存在后再调用另外某些依赖SolidWorks文件管理器打开的文档比如通过Search查到的轻量化零件在完全加载前获取CustomPropertyManager也可能得到空引用。这时可以调用swModel.ForceRebuild3或者先尝试swModel.ShowConfiguration2等强制加载机制。5.6 大批量读取时的性能优化如果你要处理的文件超过几百个性能问题会非常突出。我实测过几轮总结了四个有效优化点第一尽量用Silent模式打开文档这个上一节已经提过。第二全程使用GetAll3一次性读取全部属性避免循环调用Get方法——一次COM调用能拿到的数据绝不分十次拿。第三关闭文档用CloseDoc而不是CloseFile并立刻配合Marshal.ReleaseComObject释放。第四如果只需要属性而不需要重建模型可以设置swOpenDocOptions_Silent外加swOpenDocOptions_LoadModel组合避免不必要的界面刷新。我曾在一次项目里处理800个零件最开始用可见模式打开跑了20分钟还没完改成Silent模式并加上所有优化后总耗时不到3分钟差距非常大。6. 项目扩展下一步你可以做的三个方向这个项目做完基础的“读取全部属性”之后后续可以延展的方向很多。我这里列三个被问得最多的第一个是属性写入反向把业务数据写入SolidWorks模型。用CustomPropertyManager的Add3方法配合GetAll3已有的结构可以做到“先全量读取、按差异写入、再全量校验”形成一个完整的属性同步闭环。第二个是Excel/CSV导出把GetAll3的结果结合文件路径、配置名组织成二维表导给PLM/ERP做上游数据准备。这一步的代码量不大但要把StringComparer.OrdinalIgnoreCase这些细节处理好否则属性名大小写不一致会导致导出的列错位。第三个是属性模板校验比如公司要求每个模型必须包含“图号”“名称”“材料”“设计者”四个必填属性。用GetAll3一次性读取后检查Dictionary是否包含这四个键缺失的自动标记并生成报告。这个功能看着简单但在图纸审查阶段能省下大量人工核对时间。我在实际项目中还养成了一个习惯每次读取属性完都顺手把ConfigurationName写进日志。原因是很多诡异的属性“丢失”问题最后都发现其实是读错了配置而不是属性真的不见了。配置名加上文件名两个信息合在一起排查问题时能少走很多弯路。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑