SolidWorks二次开发C#入门:环境搭建与对象模型避坑指南
很多接触SolidWorks二次开发的C#新手最容易有的一个疑问就是我装了SolidWorks也装了Visual Studio代码也照着网上抄了为什么一运行就报错或者压根连SolidWorks都连不上我当年踩这个坑的时候翻遍了各种论坛最后才把环境、引用、对象模型这几件事彻底搞清楚。这篇文章就是把我积累下来的这些经验一次性讲透专门给准备入坑SolidWorks API开发的新手看。读完你能明白API开发到底能干什么、环境怎么搭、第一段能跑的代码怎么写、以及最核心的对象模型是怎么回事——这些都是后续所有二次开发功能的地基。1. 先说清楚SolidWorks API到底能干什么值不值得学1.1 二次开发最常见的三大应用场景SolidWorks本身已经是一款很成熟的CAD软件但再成熟的软件也没法覆盖所有人的需求。API二次开发解决的核心问题总结下来其实就三类批量重复操作、参数化自动建模、数据打通。批量重复操作最常见。比如你有500个零件需要导成STEP格式或者100张工程图需要批量转PDF手工操作半天就过去了人还容易出错。用API写一个循环挂机跑几分钟全搞定。再比如BOM表导出SolidWorks自带的BOM功能导出的格式往往不满足ERP系统的要求用API就可以直接把BOM数据按你们公司的格式输出。参数化自动建模是另外一个高频场景。很多企业做非标自动化设备零件结构相似但尺寸随项目变化。这类需求可以在SolidWorks里建好参数化模板再用C#通过API修改关键尺寸、更新模型、自动出图。设计人员只需要填一个订单参数表格模型和图纸自动生成效率提升不是一星半点。数据打通则是把SolidWorks和企业其他系统联系起来比如从PDM/PLM系统读取物料信息、把模型属性写入数据库、读取零件自定义属性生成采购清单。在这个方向上SolidWorks API扮演的就是连接器角色。1.2 宏录制、VBA宏、C#外接程序新手该从哪个开始SolidWorks提供了多种二次开发方式新手容易一上来就懵宏录制、VBA宏、C#插件、VB.NET插件到底选哪个先说宏录制。SolidWorks自带的宏录制功能可以把你的手动操作录制为VBA代码。很多老工程师教新人的套路是先手动操作一遍录制成宏然后在宏的基础上修改。这个思路用来学习API的对象模型和函数调用确实有效我到现在查API用法时偶尔还会录一段宏看看它调了哪个接口。但宏录制的代码非常冗长有很多视图刷新的噪声代码不适合直接作为正式项目的基础。VBA宏的优点是运行简单工具--宏--运行即可不需要编译环境。但它受限于SolidWorks进程内运行单线程且无法做复杂的界面交互。适合自己用的小工具、临时脚本不适合做正式交付的应用程序。C#外接程序Add-In是正规的二次开发主流方式。它可以做成独立的DLL注册到SolidWorks中启动SolidWorks时自动加载拥有完整的菜单、工具栏、任务窗格界面。也可以做成独立EXE通过API连接SolidWorks进程进行操作。C#相对VBA的优势在于完整的面向对象能力、更好的异常处理机制、方便与其他系统数据库、WebService集成、内存和性能管理更可控。我建议新手的路线是用宏录制辅助学习API调用方法然后用C#写一个最简单的连接程序跑通环境再逐步往插件方向深入。2. 开发环境搭建这一步卡住了80%的新手2.1 SolidWorks版本与Visual Studio版本该怎么匹配关于环境搭建网上说啥的都有其实核心就一句话只要你的SolidWorks是2016以后的版本用Visual Studio 2019或2022都完全没有问题不用刻意追求版本一一对应。SolidWorks从2015版本开始就全面转向.NET Framework 4.0以上API类型库基本稳定。Visual Studio 2019/2022都可以正常创建.NET Framework 4.8项目来引用SolidWorks类型库。要注意的是你创建的C#项目目标框架必须选择**.NET Framework**而不是.NET Core/.NET 5。别问我为什么知道我见过太多人创建了.NET 6项目然后发现引不了SolidWorks.Interop的COM类型库。如果条件允许建议用Visual Studio 2022 SolidWorks 2020及以上版本。这套组合我实测稳定调试体验也最好。SolidWorks版本太老比如2014年以前API接口和现代VS的支持都有一些别扭的地方新手没必要给自己加难度。2.2 引用的DLL到底是哪几个新手常常困惑我要引用哪个dll才算数SolidWorks安装目录下有一堆dll随便选行不行答案是不用去安装目录翻。在Visual Studio的引用管理器里切换到COM选项卡搜索SolidWorks你会看到下面几个主要项SolidWorks Interop sldworks这是核心API库对应SolidWorks.Interop.sldworks.dll包含SldWorks、ModelDoc2、PartDoc、AssemblyDoc等核心接口。SolidWorks Interop swconst常量库对应SolidWorks.Interop.swconst.dll包含所有枚举常量比如文件保存类型、特征类型、单位制等。在实际项目中这两个都要引用。swconst里的常量如果记不住开发时直接在代码里输入swConst_智能提示会列出一大串候选。除了这两个还有一些按需引用的库SolidWorks.Interop.swpublished用来开发Add-In插件注册用、SolidWorks.Interop.swcommands插件命令ID定义。做第一阶段的连接程序不需要它们。注意通过COM选项卡添加的引用Visual Studio会自动给这些dll启用嵌入互操作类型Embed Interop Types。对SolidWorks API开发来说这个选项最好手动改成False否则后面容易出现类型转换异常。怎么改选中引用在属性面板里把嵌入互操作类型改为False。2.3 AnythingCPU问题是64位时代最大的坑引用搞定了项目平台没选对的话程序一运行照样崩。现在的SolidWorks基本都是64位版本对应的C#项目必须设置成x64平台。Visual Studio新建的C#项目默认是AnyCPU在64位操作系统上运行时默认采用64位进程理论上没问题但新手很容易在NuGet装了某些32位依赖包后或者在调试时被VS强制切回x86然后程序一跑就报错“试图加载格式不正确的程序”。这个报错十有八九是平台目标不一致。项目设置的位置右键项目 - 属性 - 生成 - 平台目标 - 选择x64。另外一个容易忽略的点如果是64位SolidWorks在调试时把VS里的调试平台也切到x64。在工具栏的解决方案配置旁边一般有个平台下拉框确保是x64而不是x86或Any CPU。2.4 验证环境是否可用的最快方法环境搭建完最快速的验证方法是创建一个控制台项目引用上述两个Interop DLL设置x64平台然后写一段三行代码尝试连接SolidWorks。这一步跑通了环境问题就彻底翻篇了。验证代码我这里先不展开下一节详细讲。这里要叮嘱的只有一件事验证环境的项目一定用控制台应用程序而不是Windows窗体或类库。控制台项目最简单、启动最快、没有UI线程干扰最适合用来做边界测试。3. 跑通第一个C#程序连接SolidWorks并操作文档3.1 连接方式GetObject还是CreateObject这是个问题在C#里连接SolidWorks网上最常见的两种写法// 写法一获取正在运行的SolidWorks实例 SolidWorks.Interop.sldworks.SldWorks swApp Marshal.GetActiveObject(SldWorks.Application) as SldWorks.Interop.sldworks.SldWorks; // 写法二创建新的SolidWorks实例 SldWorks swApp new SldWorks();这两个写法各有应用场景但新手往往不知道该用哪个。写法一GetActiveObject连接已经打开的SolidWorks窗口。好处是快不占用额外内存坏处是如果用户没打开SolidWorks程序会直接报错获取不到活动对象。适合独立EXE程序在外部连接SolidWorks的场景。写法二new SldWorks创建一个新的SolidWorks进程。好处是不管SolidWorks有没有打开都能保证有一个可用实例坏处是如果SolidWorks已经开了再new一个就等于多开了一个SolidWorks进程内存消耗大而且两次创建的实例ID不同后续通过API操作起来逻辑会乱。我推荐的稳妥写法先尝试获取正在运行的实例如果获取不到再创建新实例。代码里用try-catch包一层判断即可。3.2 完整可运行的连接程序代码下面是一个可以直接跑通的完整案例。这个程序做了三件事连接SolidWorks、新建一个零件文档、向文档写入一行注释文字。用它来验证环境非常合适。using System; using System.Runtime.InteropServices; using SolidWorks.Interop.sldworks; using SolidWorks.Interop.swconst; namespace SolidWorksApiFirstTry { class Program { static void Main(string[] args) { // 1. 连接SolidWorks SldWorks swApp ConnectToSolidWorks(); if (swApp null) { Console.WriteLine(连接SolidWorks失败请确认软件已安装并能正常运行。); return; } // 2. 显示SolidWorks窗口如果被隐藏的话 swApp.Visible true; // 3. 新建一个零件文档 int error 0; int warning 0; ModelDoc2 swDoc swApp.NewDocument( D:\\Program Files\\SOLIDWORKS Corp\\SOLIDWORKS\\data\\templates\\零件.prtdot, 0, 0.01, 0.01, ref error, ref warning) as ModelDoc2; if (swDoc null) { Console.WriteLine($新建文档失败错误码{error}); return; } // 4. 向模型写入一行业务信息 bool ret swDoc.SetTitle3(第一次连接的测试零件); if (ret) { Console.WriteLine(文档标题设置成功); } // 5. 保存这里演示保存到D盘根目录可以自行修改路径 int saveError 0; int saveWarning 0; bool saveRet swDoc.Extension.SaveAs( D:\\first_part.SLDPRT, 0, (int)swSaveAsOptions_e.swSaveAsOptions_Silent, null, ref saveError, ref saveWarning); if (saveRet) { Console.WriteLine(零件已保存D:\\first_part.SLDPRT); } else { Console.WriteLine($保存失败错误码{saveError}); } Console.WriteLine(按任意键结束程序...); Console.ReadKey(); } static SldWorks ConnectToSolidWorks() { try { // 尝试获取正在运行的SolidWorks实例 object obj Marshal.GetActiveObject(SldWorks.Application); if (obj ! null) { return obj as SldWorks; } } catch { // 没有正在运行的实例进入catch逻辑 } // 创建新的SolidWorks实例 try { SldWorks swApp new SldWorks(); return swApp; } catch (Exception ex) { Console.WriteLine(创建SolidWorks实例失败 ex.Message); return null; } } } }提示NewDocument的模板路径绝不能照抄我代码里的。每个机器SolidWorks安装位置不同模板路径也不同。正确的获取方式见下方第3.3节。3.3 新手的第一个拦路虎模板路径变硬编码了运行上面的代码你大概率会遇到的问题是找不到模板文件错误码非0或直接抛异常。这是因为每个SolidWorks版本的模板路径都不一样你电脑上的模板不一定在D盘那个路径。正确的做法是从已经打开的SolidWorks实例获取默认模板路径而不是自己猜。改法如下// 获取默认模板路径 string defaultTemplate (string)swApp.GetUserPreferenceStringValue( (int)swUserPreferenceStringValue_e.swDefaultTemplatePart); // 用默认模板新建零件 ModelDoc2 swDoc swApp.NewDocument( defaultTemplate, 0, 0.01, 0.01, ref error, ref warning) as ModelDoc2;GetUserPreferenceStringValue这个方法在后续开发中也会频繁用到它用来读取SolidWorks的各种用户设置。这里我们读的是默认零件模板路径拿到之后直接传给NewDocument就再也不会出现模板文件找不到的问题了。3.4 调试时如何验证程序确实连上了SolidWorks程序跑起来后如果你看到SolidWorks窗口自动弹出来并且新建了一个零件那就说明连接成功、基本对象调用成功。这时环境算是彻底通过了。如果你想进一步确认可以在代码里加一个判断语句输出当前SolidWorks的版本号和当前文档名。Console.WriteLine($SolidWorks版本{swApp.RevisionNumber()}); Console.WriteLine($当前文档标题{swDoc.GetTitle()});这里顺便提一个很多新手都会遇到的问题程序一退出SolidWorks也自动退出了。为什么因为你的进程通过new SldWorks()创建了SolidWorks实例当你的程序进程结束时这个实例失去了创建者部分系统配置下会自动退出。如果不想这样可以在程序退出前把SolidWorks主窗口显示出来或者在代码里设置swApp.Visible true之后不要再隐藏它。如果是用GetActiveObject连接已经打开的实例就不存在这个问题。4. 理解SolidWorks API的对象模型层级这是整个二次开发的核心地图4.1 从SldWorks到Feature整个API就是一棵树一个SolidWorks文档在API层面是怎么组织数据的搞懂了这点后面所有功能开发就等于拿到了地图。SolidWorks API整体是树状结构从上往下大致是这样的层级类/接口作用类比应用层SldWorks管理SolidWorks进程、文件操作、系统设置整个软件的操作系统文档层ModelDoc2 / ModelDocExtension管理文档文件对应零件、装配体、工程图一个打开的Word文件子文档层PartDoc / AssemblyDoc / DrawingDoc访问特征树、配合关系、视图等Word里的段落和图片特征层Feature所有建模步骤拉伸、旋转、圆角都在这Word里的一个对象文本框/图表几何层Body / Face / Edge / Vertex访问实体几何拓扑信息对象内部的文字和坐标数据在代码里最核心的就是这个对应关系SldWorks应用- ModelDoc2文档- Feature特征- Body/Face等几何。日常开发中几乎一半以上的功能都是围绕这三层之间的跳转和遍历展开的。4.2 从文档到特征遍历一个零件里的所有特征看一段最典型的遍历代码。这段代码能把当前零件文档里所有特征的名称打印出来ModelDoc2 swDoc swApp.ActiveDoc as ModelDoc2; if (swDoc null) return; Feature swFeat swDoc.FirstFeature() as Feature; while (swFeat ! null) { Console.WriteLine($特征名称{swFeat.Name}); // 如果特征有子特征比如阵列产生的一组特征也需要遍历 Feature subFeat swFeat.GetFirstSubFeature() as Feature; while (subFeat ! null) { Console.WriteLine($ ├─ 子特征{subFeat.Name}); subFeat subFeat.GetNextSubFeature() as Feature; } swFeat swFeat.GetNextFeature() as Feature; }这套遍历套路就是经典的链表遍历模式先取第一个然后不断调用GetNext直到返回null。SolidWorks API很多集合都钟爱这种模式包括特征、面、边、视图等等。记住这个模式后面能省很多时间。4.3 按名称查找对象是新手最容易懵的地方有时候你不需要遍历所有特征只需要找到某一个叫拉伸1的特征改它的尺寸。用名称查找比遍历高效得多。关键方法是ModelDocExtension接口下的FindFeatureByName或者广义的FindObjectByNameModelDocExtension swExt swDoc.Extension; Feature feat swExt.FindFeatureByName(拉伸1) as Feature; if (feat ! null) { Console.WriteLine($找到了特征{feat.Name}); }这里有个隐藏的小陷阱要提醒特征名称不是唯一的。同一个SolidWorks文档里不同层级的特征比如零件里的特征和阵列里的子特征名称可能相同。FindFeatureByName默认找到的是第一个匹配项。如果文档结构复杂精确查找还是得走遍历同时比对特征类型和上游下游关系。4.4 SelectionManager和SolidWorks界面交互的桥梁做二次开发经常遇到一个需求用户在SolidWorks界面里手动点选了一个面你的程序要读出这个面的面积。这就要用到SelectionManager。// 获取用户当前选中的对象 SelectionMgr selMgr swApp.ISelectionManager as SelectionMgr; int count selMgr.GetSelectedObjectCount2(-1); for (int i 1; i count; i) { object selObj selMgr.GetSelectedObject6(i, -1) as object; if (selObj is Face2) { Face2 face selObj as Face2; double area face.GetArea(); Console.WriteLine($选中了面面积是{area} 平方米); } }GetSelectedObjectCount2和GetSelectedObject6里的第二个参数是标记值传-1表示忽略标记取所有选中对象。如果你是编程向的选择代码里通过Select4选中可以设置不同的标记值来区分用途——比如你先选中一个面当目标面再选中一条边当方向参考就可以分别标记为1和2然后用标记值过滤取出对应的对象。选中的对象返回类型是object使用前一定要用is/as做类型判断。你永远不知道用户选的是面、边、还是草图点。4.5 属性CustomProperty操作和PDM/ERP对接的基础很多二次开发项目最终都要读取或写入文件的自定义属性比如零件号、材料、设计师、版本号。// 写入自定义属性 string configName ; bool ret swDoc.SetCustomProperty(configName, 零件号, PART-001); ret swDoc.SetCustomProperty(configName, 材料, Q235); // 读取自定义属性 string partNo swDoc.GetCustomProperty(configName, 零件号) as string; Console.WriteLine($零件号{partNo});需要注意configName参数。如果你传入空字符串读写的是默认配置的自定义属性。如果你的模型里存在多个配置Configuration必须指定配置名否则可能出现属性读不到或者读到默认配置的属性。这一点在从装配体里循环读取每个零件属性时尤其重要。5. 新手阶段最容易踩的坑崩溃、类型不匹配、API文档看不懂5.1 为什么我的程序一运行SolidWorks就崩溃这是新手反馈最多的问题。SolidWorks异常崩溃的原因很多但二次开发导致的崩溃最常见的是这几类COM对象生命周期管理不当。SolidWorks API本质是COM接口C#通过运行时互操作调用。如果一个COM对象被垃圾回收机制在错误的时间点释放了SolidWorks进程可能就崩溃了。这个问题在循环遍历大量特征/面/凸台时尤其容易出现。规避方式避免在循环内频繁调用Marshal.ReleaseComObject让运行时自己管理大部分对象即可。只有在处理大量几何对象几万条边时才考虑配合Marshal.ReleaseComObject手动释放。在错误的时间操作模型。比如在模型重建Rebuild过程中去访问特征树或者在文档尚未加载完成时就去读属性。新手常犯的错是新建文档后立即访问它的特征其实文档虽然返回了但内部还没初始化好。稳妥做法是新建/打开文档后先swDoc.ForceRebuild3(false)重建一次或者用循环等待swDoc.GetType()! 0确认文档状态正常。5.2 “调用目标发生了异常”和“Object reference not set”到底是谁的锅这两行错误信息几乎每个二次开发新手都见过。我的经验是调用目标发生了异常TargetInvocationException大多是COM异常被封装成了.NET异常。表面上看是抛了异常背后往往是调用的API本身返回了失败或者参数类型不对。解决思路是捕获异常后看InnerException大多数情况下真实原因在InnerException里。如果一个API调用在界面上手动操作能成功但程序调用失败优先怀疑参数类型和null值。Object reference not set to an instance of an objectNullReferenceException更是家常便饭。SolidWorks API很多方法如果执行失败不会抛异常而是直接返回null。新手容易直接拿返回结果继续操作于是空引用异常就出现了。养成习惯每个API调用返回的对象先判空再使用。别看这习惯简单能帮你省掉一晚上排查时间。5.3 类型库版本冲突为什么同一份代码换个电脑就报错在一个项目里如果你同时引用了通过COM添加的Interop DLL和从SolidWorks安装目录直接添加的DLL版本不同会出现强名称签名冲突或者类型定义无法解析这一类问题。另外SolidWorks的Interop类型库是强签名的如果一个类型被两个不同的程序集各自定义了一份CLR就分不清了。避免这个问题的办法简单只通过COM选项卡添加Interop引用并且把嵌入互操作类型设为False。不要混着引用不同路径下的同一系列DLL。5.4 怎么查API文档效率最高最后说说文档查询。SolidWorks安装目录下自带API帮助文档一般在SolidWorks安装目录\api\apihelp.html。但说实话必须连接到SolidWorks官网的在线API帮助才能查看具体方法和属性本地端只有索引。我的查阅习惯是这样的优先用VS的智能提示。输入对象名小数点弹出的方法列表里基本能看到所有可用API配合XML注释足够解决80%的问题。不确定参数含义时打开API帮助查具体方法。在API帮助的索引里输入方法名比如SelectByID2能看到参数说明、返回值含义和示例代码。录一段宏看SolidWorks自己怎么调。这一步真的非常管用。比如我想知道保存PDF用哪个API手动操作一遍同时录制宏录出来的VBA代码里直接就有ExportToPdf之类的调用翻译成C#只是语法层面的转换。遇到属性Property和接口Interface的多版本问题比如ModelDoc2和ModelDocExtension很多新功能只在Extension接口里有。习惯上优先使用ModelDocExtension因为它是后来扩展功能的主入口。6. 从控制台程序到真正的插件新手下一步该往哪走6.1 控制台程序能做什么做不了什么学会了上面这些内容你已经可以用控制台程序完成很多实用工具了。比如批量转格式、批量读取属性、自动生成BOM等这类独立运行的程序本质上不依赖SolidWorks界面适合做后台批处理工具。但控制台程序有两个先天局限一是它需要独立启动无法被集成到SolidWorks的菜单和工具栏里二是它运行起来之后界面交互能力弱——无法添加窗体和控件无法让用户在SolidWorks里点选-执行-反馈这种交互式操作。如果你的工具需要设计人员经常用、反复用做成插件Add-In才是正道。6.2 Add-In开发的核心要点提前预告在SolidWorks里注册一个Add-In插件核心要解决三件事第一实现ISwAddin接口。这是SolidWorks加载插件的契约。插件类继承这个接口后需要实现ConnectToSW和DisconnectFromSW两个方法。前者在插件加载时被调用用于初始化菜单和命令后者在插件卸载时被调用用于清理资源。public class MyAddin : ISwAddin { public bool ConnectToSW(object ThisSW, int Cookie) { // 保存SolidWorks应用对象、Cookie // 调用CommandManager注册命令、菜单、工具条 return true; } public bool DisconnectFromSW() { // 注销命令、释放资源 return true; } }第二注册表注册与swAddin文件。要在SolidWorks中识别你的插件需要修改注册表在HKEY_CURRENT_USER\Software\SolidWorks\AddIns下新建项写入插件IDGuid和DLL路径。同时SolidWorks要求插件DLL旁边放一个.swAddin文件里面记录了插件的ID和加载方式。第三步命令ID分配。SolidWorks插件的每个菜单、按钮都绑定一个命令ID是从swCommands_e常量范围里分配出来的。注册菜单、绑定命令ID、处理点击事件回掉这个环节对新手来说很容易绕晕后面我会单独用一篇详细讲——这也是我把这个系列定位为新手引导的一部分。6.3 中间过渡方案WinForm独立程序先顶一阵子如果你暂时不打算踩Add-In的坑但又想做带界面的工具一个折中方案是用C#写一个WinForm独立程序界面放在自己程序的窗体里通过API连接SolidWorks用户点击窗体上的按钮程序操作SolidWorks完成对应功能。这个是很多公司轻量化二次开发的典型做法。优点是开发难度比Add-In低一个量级不需要注册表不需要理解命令ID机制调试也简单。缺点是每次都要先启动程序再连接SolidWorks且交互体验远不如原生插件。我个人的建议是如果你的工具预期只服务你自己或者同一个办公室里的几个人独立WinForm程序完全够用。如果工具要大范围铺开、甚至要打包安装包交付给客户那还是老老实实做Add-In插件。这个系列下一步的文章会分别讲Add-In从零到注册成功的完整过程、以及如何用TaskPane做自定义UI界面。先把这篇文章里的环境搭建和第一个程序跑通你在SolidWorks API开发这条路上就算是正式入了门。有事没事打开Visual Studio敲一敲把遍历特征、读写自定义属性这些基础操作练到闭眼能写后面学什么都快。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →