这段时间在写一个自动处理三维模型的工具绕不开 SolidWorks 的 API。网上资料零零散散要么是宏代码要么是VB写的老古董C# 直连的完整示例很少。自己踩了一堆坑之后把最实用的“直连”路线整理出来包含可以直接复制的代码和几个必须注意的坑。这篇文章适合两类人一类是想脱离手工打开 SolidWorks 挨个导图纸、改属性的工程师另一类是刚接触 SolidWorks 二次开发想用 C# 写第一个能跑的连接程序。我会尽量把“为什么这么做”也讲清楚这样你改起来不慌。1. 直连 SolidWorks 的整体思路为什么选 C# 和 COM1.1 直连解决的痛点从文件交换到 API 自动化很多人处理 SolidWorks 和外部系统的数据交换第一反应是导出中间格式比如 STEP、IGES或者读自定义的文本文件。这种方法最简单但缺点很明显丢特征、丢装配关系而且没法从模型里反向写入数据。举个例子你要批量读取一百个零件的质量属性或者改掉几十个文件的“材料”属性靠文件导出基本做不到。直连的意思就是让 C# 程序通过 COM 接口直接驱动 SolidWorks 应用。你可以把 SolidWorks 想象成一台带遥控器的机器COM 接口就是遥控器上那些按钮。按下“打开文件”的按钮模型就打开了按下“读取属性”的按钮数据就返回回来了。机器仍然是 SolidWorks只是操作者从人变成了程序。这种方式的优势在于不产生中间文件直接在内存中操作速度快。能覆盖 SolidWorks 的绝大部分原生功能不只停留在读写文件。可以进行批量、循环、带判断逻辑的复杂操作。能把你手工操作的标准流程固化下来避免重复劳动和误操作。劣势同样存在必须本机装有 SolidWorks 且授权可用版本兼容性需要留意COM 对象是进程外调用性能有一定消耗。但对大多数内部自动化工具来说这些都可以接受。1.2 早期绑定与后期绑定的取舍C# 调用 COM 组件有两种常见姿势加引用和用 dynamic。加引用也就是在 Visual Studio 里右键“添加引用”选“COM 库”找到 SolidWorks 的 Interop 类型库。这种方式叫早期绑定编译器直接生成强类型接口代码里能看到 IntelliSense属性方法不会写错运行时的性能也好一些。缺点是你的开发机必须安装对应版本的 SolidWorks且配置好类型库否则引用加不上。用 dynamic 调用通过 Activator.CreateInstance 或类型库加载器传入 ProgID创建一个运行时对象再用 dynamic 去调用方法。这种方式叫后期绑定代码上省掉了强类型引用的麻烦但代价是没有智能提示写错了只能运行时才发现而且性能稍差。我的建议是做“简单实现”时优先用早期绑定。原因很直接开发效率高不容易因为拼写错误卡半天。但有个前提你编译出来的程序目标机器上的 SolidWorks 主版本要和你引用类型库的版本一致或兼容。否则运行时会提示“方法不存在”之类的异常。关于版本问题我们后面单独说。1.3 最小方案的总体流程所谓“简单实现”我把它拆成四个步骤连接获取或创建一个 SolidWorks 应用对象。打开用 OpenDoc6 打开指定模型文件。操作读取或修改模型数据比如拿文件名、质量、自定义属性。清理保存文档、关闭文档、释放 COM 引用。下面所有的代码都以这四个步骤为主线。你完全可以把这段代码当模板跑通之后再往里面加自己的业务逻辑。2. 环境准备跑通直连前要做的三件事2.1 确认 SolidWorks 版本与 .NET Framework这一步容易忽略但直接影响结果。SolidWorks 官方 API 通常每个主版本对应一组互操作类型库比如 SolidWorks 2020 和 SolidWorks 2021 的接口版本不一定完全一样。你自己开发时最好保证开发机的 SolidWorks 版本和最终运行环境一致。如果没法一致起码主版本要相同比如都是 2020。然后是 .NET 版本。SolidWorks 的 COM 接口是老牌 COM理论上所有 .NET 版本都能调。但如果你用的是 .NET 6/8 这类新项目添加 COM 引用时会发现“互操作程序集”的生成和旧的 .NET Framework 项目不太一样有些类型库可能识别不全。我在实际项目里最稳的组合是.NET Framework 4.8 x64 控制台应用。SolidWorks 本身是 64 位进程你的程序最好也编译成 x64否则在某种环境上可能出现位元不匹配的异常。如果你非要用 .NET Core 或 .NET 5也不是不行需要手动引用互操作 DLL或者用 dynamic 方式绕过引用。但这会增加复杂度不适合“简单实现”的目标。我建议先老老实实用 .NET Framework 4.8。2.2 在项目中添加 SolidWorks COM 引用打开 Visual Studio创建一个控制台应用.NET Framework然后在解决方案资源管理器里右键项目选择“添加引用”左侧选“COM”在列表里找到类似“SolidWorks 2020 Type Library”的项。不同版本显示的名称略有差异SolidWorks 版本类型库名称示例2018SolidWorks 2018 Type Library2020SolidWorks 2020 Type Library2022SolidWorks 2022 Type Library勾选后确认Visual Studio 会在项目的 bin 目录下生成 Interop.SldWorks.dll 之类的文件。这里有个坑列表里可能会出现多个相似的条目比如“SolidWorks 2020 Type Library”和“SolidWorks 2020 Constant Type Library”。我们要选的是前者。后者只是常量定义单独引用它没有用。如果两个都勾选了也没关系主要是前者的接口。添加完引用后代码里 using SldWorks; 就能使用 SldWorks 命名空间下的类型了。2.3 设置启动权限与运行方式SolidWorks 是个桌面应用直接连它和普通后台服务不一样。如果你的程序想在没有管理员权限的情况下运行基本没问题。但如果你跑的是自动化脚本且需要操作 SolidWorks 正在使用中的文件建议以管理员身份运行一次程序确认权限。也可能遇到的情况是SolidWorks 以管理员权限运行了而你的程序不是管理员权限那么 COM 调用会失败。反过来也一样。解决办法很简单要么都普通权限要么都管理员权限。另外如果你是在服务器上想用 SolidWorks API注意 SolidWorks 的授权通常不允许无界面服务器调用而且 COM 的窗口消息机制在非交互会话下不稳定。我一般只在有桌面的环境里做自动化不做无人值守服务。3. 核心代码从连接到打开模型的最小实现3.1 连接已运行的 SolidWorks 实例先看最简单的场景你的电脑上已经手动打开了一个 SolidWorks 窗口程序要去“依附”它。在 COM 里我们透过“运行对象表”来拿到正在运行的实例。C# 里可以直接用 Marshal.GetActiveObject。using System; using System.Runtime.InteropServices; using SldWorks; namespace SolidWorksConnector { class Program { static void Main(string[] args) { SldWorks.ISldWorks swApp null; try { swApp (SldWorks.ISldWorks)Marshal.GetActiveObject(SldWorks.Application); Console.WriteLine($连接成功版本{swApp.RevisionNumber()}); } catch (Exception ex) { Console.WriteLine($连接失败{ex.Message}); } } } }这段代码里SldWorks.Application是 SolidWorks 的 ProgID。只要 SolidWorks 正在运行GetActiveObject 就能拿到它的顶层对象。注意这里用到的类型是 ISldWorks不是 SldWorks.ApplicationClass。这也是很多人容易踩坑的地方从类型库里看应用对象有两个“长得很像”的类型一个是接口一个是 CoClass。接收返回值时用接口接最安全。如果你的程序連不上现有实例通常是因为 SolidWorks 根本没开或者权限不匹配。后面我们细说。3.2 创建新的 SolidWorks 应用实例更多自动化场景里我们不想让 SolidWorks 一直开着而是让程序自己拉起一个新进程。这就要用 Type.GetTypeFromProgID 加 Activator.CreateInstance。Type swType Type.GetTypeFromProgID(SldWorks.Application); SldWorks.ISldWorks swApp (SldWorks.ISldWorks)Activator.CreateInstance(swType);创建出来的对象默认是不可见的因为 SolidWorks 启动时以后台模式运行会先把主窗口隐藏起来。如果你需要看到界面可以调用swApp.Visible true。注意 ISldWorks 接口里有个Visible属性布尔型直接赋值即可。提示用后台模式跑模型处理时建议保持swApp.Visible false处理速度更快也不会弹一堆窗口干扰桌面。需要调试时再临时改成 true。不管是 GetActiveObject 还是 CreateInstance拿到的 swApp 都是同一个顶层入口。后续操作完全一致。3.3 打开零件/装配体并读取基础信息打开文件我们使用 OpenDoc6。这个方法是 SolidWorks API 里打开文档最标准的方式参数非常多前面五个是关键后面的可以给默认值。public const int swDocPART 1; public const int swDocASSEMBLY 2; public const int swDocDRAWING 3; string filePath D:\Models\TestPart.SLDPRT; int errors 0; int warnings 0; ModelDoc2 modelDoc swApp.OpenDoc6( filePath, swDocPART, (int)swOpenDocOptions_e.swOpenDocOptions_Silent, , ref errors, ref warnings); if (modelDoc ! null) { Console.WriteLine(打开成功 modelDoc.GetTitle()); }参数解释第一个文件完整路径。第二个文档类型。1 零件2 装配体3 工程图。第三个打开选项。swOpenDocOptions_Silent表示安静打开不显示保存提示等弹窗。这个值在不同版本里都是一个整数常量可以直接写 0 也能打开。第四个配置名称。默认留空字符串即可。第五、第六个错误和警告需要ref传出调用前先定义变量。打开成功后返回的是一个ModelDoc2对象。这个对象包含了模型文档的几乎所有操作入口比如Extension、GetFeatures()、GetCustomPropertyValues等。拿到模型对象后第一件想做的事通常是读质量属性。用Extension.GetMassProperties可以拿到很多工程数据。object[] props modelDoc.Extension.GetMassProperties( (int)swMassPropertyType_e.swMassPropertyType_Regular, null, false, null, null, null); // props 数组里包含体积、面积、质量等信息 Console.WriteLine($质量{props[2]} kg);GetMassProperties 的返回值比较特殊是一个 object 数组下标从 0 开始依次是体积、表面积、质量等。这个数组对应关系在不同版本里基本稳定但建议只读取你需要的项并且加一个异常保护。3.4 遍历特征树与属性提取特征树是 SolidWorks 模型的结构核心。通过modelDoc.FeatureByPositionReverse(0)可以按顺序取到每个特征。遍历的代码很简短但要注意顺序默认是从特征树最末位开始向前取如果你想从顶部开始可以用FeatureByPositionReverse(treeLen - 1)之类的循环。我实际操作时更常用的是先拿特征树的数量再从头遍历。int featCount modelDoc.GetFeatureCount(); for (int i featCount - 1; i 0; i--) { Feature feat modelDoc.FeatureByPositionReverse(i); Console.WriteLine($特征{feat.Name}类型{feat.GetTypeName()}); }这里有个细节FeatureByPositionReverse(i)当 i 从 0 开始到 featCount-1依次返回的是特征树末端的特征。如果你想从头开始就倒着传 i。自定义属性的读取在工程图标题栏、BOM 表格场景里太常用了。模型的自定义属性分为“配置特定”和“摘要”两类。读取配置特定属性的方法如下string val modelDoc.CustomPropertyManager().Get(材料).Value;其中CustomPropertyManager()传空字符串代表默认配置。如果你的模型有多个配置可以把配置名字传进去。如果属性不存在Value 可能是空字符串或特殊值需要自己处理。3.5 保存与关闭的正确姿势处理完模型后保存和关闭别随便调。如果直接modelDoc.Close()程序不会提示但 SolidWorks 内部会判断是否保存过直接关闭会丢失改动。正确做法是先主动保存或另存为。// 保存组件 modelDoc.Save(); // 另存为 STEP 文件如果你有这个需求 modelDoc.SaveAs(D:\Output\TestPart.STEP);SaveAs有个重载会把它当成另一种格式对待。但注意SaveAs 的第二个参数如果传 0表示保存当前格式如果你要另存为 STEP需要传对应的格式值。通常我更推荐用swDoc.SaveAs3方法它的参数更明确可以指定swSaveAsOptions。关闭文档用swApp.CloseDoc(modelDoc.GetTitle())或者modelDoc.Close()。我一般用前一种因为 CloseDoc 传入文档名称即可避免引用的对象状态不清。最后无论有没有异常都要在程序结束前释放 COM 资源。这是一个看起来简单但很多新手会漏掉的点。Marshal.FinalReleaseComObject(modelDoc); Marshal.FinalReleaseComObject(swApp); GC.Collect(); GC.WaitForPendingFinalizers();不过要注意如果你激活的是已经手动打开的 SolidWorks 实例千万别调用swApp.ExitApp()。否则你直接把用户的 SolidWorks 给关了。只有自己创建的新实例才应该调用ExitApp()。4. 进阶技巧与实用场景扩展4.1 批量处理文件夹中的模型直连的价值不在单文件而在批处理。假设你要把一个文件夹下所有 SLDPRT 文件的质量读取出来逻辑就是把上面的打开代码包到一个循环里。string[] files System.IO.Directory.GetFiles(D:\Models, *.SLDPRT); DataTable dt new DataTable(); dt.Columns.Add(文件, typeof(string)); dt.Columns.Add(质量kg, typeof(string)); foreach (string file in files) { int error 0, warn 0; ModelDoc2 doc swApp.OpenDoc6(file, swDocPART, (int)swOpenDocOptions_e.swOpenDocOptions_Silent, , ref error, ref warn); if (doc ! null) { object[] props doc.Extension.GetMassProperties( (int)swMassPropertyType_e.swMassPropertyType_Regular, null, false, null, null, null); dt.Rows.Add(file, props[2]); swApp.CloseDoc(doc.GetTitle()); } }批量操作时有三个细节处理完一个文件要立刻 CloseDoc不然内存占用会越来越高。如果模型比较大可以在打开前用swOpenDocOptions_ReadOnly选项防止误改原文件。遇到打不开的文件不要终止循环记录一行错误继续下一个。你会发现这个批量处理的循环结构几乎可以套用在所有 SolidWorks 自动化需求上改属性、导出工程图、更新质量都一样。4.2 与 WinForm 进度条集成控制台程序适合做后台逻辑但给同事用或者自己看进度时带个界面友好得多。WinForm 里SolidWorks 的 COM 调用是同步的会阻塞 UI 线程。如果你直接在按钮事件里跑一个大批量循环界面就会卡死。解决办法有两个。简单粗暴的把循环体放到Task.Run里但在 Task 里访问 UI 线程控件需要小心可以用Invoke。更推荐的做法用 BackgroundWorker 或 System.Windows.Forms.Timer 做进度刷新。我的做法是在Task.Run里跑任务用IProgressint上报进度UI 层订阅这个事件。这样既能解耦又不容易出跨线程问题。SolidWorks 的 COM 调用本身是线程安全的吗严格说不是。COM 对象的调用最好在单线程模型里完成所以 Task.Run 里的循环内不要再并发调用保持一个线程处理整串操作就没问题。4.3 从文档属性中读取配置用于 BOMBOM物料清单是制造业里最常用的东西。很多公司会把零件号、材料、重量放在自定义属性里。直连之后你可以直接读取这些属性不用再手工一个个看。刚才提到的CustomPropertyManager已经能读默认配置。如果你的模型存在多个配置每个配置的属性可能不一样需要遍历配置。string[] configNames (string[])modelDoc.GetConfigurationNames(); foreach (string config in configNames) { CustomPropertyManager cpm modelDoc.CustomPropertyManager(config); string partNo cpm.Get(零件号).Value; Console.WriteLine($配置{config}零件号{partNo}); }注意GetConfigurationNames()返回的是数组但某些旧版本 API 返回object[]用之前先转一下。这个功能在自动出 BOM 时非常有用配合 Excel 导出就能做一个自动化的 BOM 生成工具。5. 常见问题与排查速查表5.1 类型库加载失败与版本不匹配现象添加引用时找不到 SolidWorks 类型库或者编译时提示“未能嵌入互操作类型”。原因与对应处理电脑没装 SolidWorks或安装的版本太旧。必须装完整版精简版可能不带类型库。类型库名称被版本数字分隔列表里搜不到可以按“SolidWorks”关键词模糊搜索。Visual Studio 的“嵌入互操作类型”属性被设置成了 True。在老项目中经常会因为嵌入互操作类型导致 COM 接口转换报错。解决办法选中引用在属性面板把“嵌入互操作类型”改成 False。5.2 连接不到正在运行的 SolidWorks现象Marshal.GetActiveObject抛出“操作无法使用”或“找不到 ActiveObject”。常见原因SolidWorks 未启动必须先启动一个实例。你的程序和 SolidWorks 权限不一致一个管理员一个普通。统一起来。跨进程调用时目标类型 ProgID 拼写错误。检查字符串是SldWorks.Application不是SolidWorks.Application。5.3 对象释放不干净导致进程卡死现象程序执行完后SolidWorks 进程仍在后台或内存居高不下。原因COM 对象没有彻底释放。SolidWorks API 对象树很复杂子对象ModelDoc、Feature、Extension都需要释放。最简单的兜底方案Marshal.FinalReleaseComObject(doc); Marshal.FinalReleaseComObject(app); GC.Collect();但如果你反复操作了很多次某个对象没被引用到还是可能出现进程残留。我建议写一个清空所有封装对象的 Dispose 方法把每层对象都置为 null然后用 GC 强制回收两次。另外程序退出时正常调用swApp.ExitApp()也能解决大部分问题——前提是这实例是你自己创建的。5.4 运行权限与后台模式注意事项很多后台自动化脚本用 Windows 计划任务触发。计划任务在系统启动时运行运行上下文可能是非交互会话SolidWorks 的 COM 调用会失败。务必设置成“只在用户登录时运行”并且勾选“使用最高权限运行”。否则你可能会看到程序启动后毫无反应进程列表里却有一个僵死的 SolidWorks。另一个奇怪的问题SolidWorks 窗口不可见时某些对话框服务会等待。比如打开文件时缺省模板、缺少参考引用会弹模态对话框。如果 SolidWorks 不可见对话框会一直挂在后台程序看起来像卡死了。应对方式是打开时使用swOpenDocOptions_Silent必要时再配合swOpenDocOptions_AutoRecovery之类的选项尽量抑制弹窗。5.5 常见异常与解决对照异常信息通常原因处理方式拒绝访问权限不足或 COM 未初始化使用管理员权限或在 Main 开头调用SetProcessDPIAware不是必要的重点检查权限方法“OpenDoc6”没有重载引用了错误版本的 Interop检查 Early Bound 类型库版本或用 dynamic对象引用未设置为实例没有创建 swApp 或连接失败用调试断点检查 swApp 是否为 null内存耗尽批量循环没有释放对象每个文件操作完立即 CloseDoc FinalReleaseComObject这些大多不是算法问题纯粹是 COM 开发的“日常”。遇到别慌按表格先排查大部分能很快解决。6. 一些心得体会6.1 用事件加回调提升稳定性SolidWorks API 提供了事件接口比如文档打开、保存、选择变化等。如果你的自动化工具需要监控用户的动作可以用事件绑定。但事件会引入进程间消息循环处理不当容易让程序变卡。我的经验是简单实现阶段先不要碰事件用主动轮询的方式去查状态已经够了。等核心流程稳定后再按需绑定特定事件。6.2 调试技巧让 SolidWorks 窗口可见后台模式虽然快但调试时什么也看不到出了问题也不好分析。建议开发阶段在swApp.Visible true下运行并且把 SolidWorks 的界面布置成你熟悉的布局。这样每一步操作的效果都能直接看到能明显减少排查时间。等代码稳定要交付时再切回不可见模式。6.3 后续扩展方向这套直连基础打好之后可以扩展的方向不少自动出工程图调用DocExport或SaveAs输出 PDF/DWG。自动更新“设计者”“日期”等属性用于版本管理。从 SolidWorks 里读取装配体约束关系同步到自研的 BOM 系统。配合Async模式做模型校核批量做干涉检查。我个人认为C# 直连 SolidWorks 最大的价值是“把重复手工操作变成可重复的脚本”。不要一上来就想做大而全的框架先搞定最简单的“连接—打开—读取—关闭”这条链路就能应付大部分日常工作。最后说一个我在实际项目中踩过的小坑打开文件后如果程序异常退出SolidWorks 可能一直报“该文件已在另一处打开”这时手动关掉残留进程即可不用重启电脑。后来我写了个 try-catch 和 finally把关闭文档和释放对象都放在了 finally 里这个问题就基本绝迹了。希望这份最小的实现能帮你少走点弯路。
阅读完成 · 觉得有帮助?