
这个系列标题我已经挂在草稿箱里有一阵子了。在论坛和社群里答复过不少这种问题“我用C#能不能控制SolidWorks自动建模”“为什么我按教程写的代码连不上SolidWorks”“VB我会一点C#到底怎么下手”说实话SolidWorks API的资料不算少但要么是官方文档那种信息密度极低的白皮书风格要么就是上来直接贴一个几百行的插件工程新手根本看不进去。所以我想写一套真正面向新手的引导第一篇先把最基础的环境、连接、运行流程讲清楚让你能在半小时内用C#写出一个能读取当前模型特征的独立程序。读完这篇文章你会知道API二次开发到底是什么形态、为什么首选C#、开发环境怎么配、怎么连接SolidWorks以及怎么通过宏录制加速自己的API学习速度。1. 二次开发整体思路先确定你要做“独立程序”还是“插件”1.1 SolidWorks API到底是什么SolidWorks本身是图形软件窗口上的每一次点击、每一项参数修改背后其实都对应着一条或一组命令。API做的事情就是把用户手动操作变成代码调用让你在程序里指挥SolidWorks干同样的活。比如“打开一个零件”“遍历特征树”“修改自定义属性”“批量另存为其他格式”这些都能通过API完成。这套接口在底层是COM组件模型官方提供的类型库文件在SolidWorks安装目录下主要就几个SolidWorks.Interop.sldworks.dll、SolidWorks.Interop.swconst.dll前者是接口定义后者是各种枚举常量的定义。C#通过COM互操作直接引用这些程序集就能像调用普通类库一样调用SolidWorks的功能。这一点决定了后续所有开发方式的走向。1.2 独立程序和插件怎么选围绕SolidWorks API业界有两种典型的开发形态独立程序.exe自己起一个进程通过COM接口连接正在运行的SolidWorks实例或者创建新的SolidWorks进程。程序与SolidWorks各自独立调试方便适合做批量工具、辅助工具、数据处理脚本。插件Add-in.dll以DLL形式注册到SolidWorks进程内在SolidWorks菜单栏或工具栏挂上自己的功能按钮随SolidWorks启动而加载适合做需要深度集成、交互频繁的正式产品。新手我强烈建议从独立程序开始。原因很简单独立程序就是一个控制台或WinForm程序主流程清晰断点调试不会拖垮SolidWorks本体出错时最坏就是重启一下SolidWorks。插件开发涉及COM注册、SwAddin接口实现、命令管理器、图标资源等一系列繁琐机制很多东西要自己搭骨架对新手来说负担太重。这个系列的前几篇都围绕独立程序展开等后面专门安排一篇讲插件工程的搭建。对于“C#为什么能外挂SolidWorks”这个疑问本质就是C#借助COM互通协议拿到SolidWorks暴露出的对象模型然后在进程外调用这些COM接口。C#在托管语言里和COM互操作已经算非常成熟的了语法清晰有强类型提示开发效率明显高于VB。2. 环境准备与引用配置2.1 开发环境的基本选型先说一下我建议的开发组合这套组合经过大量项目验证踩坑最少SolidWorks你用的正常版本即可。但要注意SolidWorks版本和API程序集是有对应关系的装哪个版本就用哪个版本的程序集后面会细说。Visual Studio 2019或2022社区版就够用。项目类型控制台应用即可用来跑第一个测试样例后面做界面的话再切WinForms或WPF。目标框架选用.NET Framework 4.7.2或4.8不建议用.NET 6/8做SolidWorks互操作的入门学习。为什么框架上我特别强调.NET Framework因为SolidWorks的官方COM接口和大量历史文档、示例都是基于.NET Framework时期的技术栈。.NET Core/5虽然也能通过ComWrappers之类的机制做COM调用但坑非常多网上能找到的现成代码往往也不直接兼容新手很容易卡在莫名其妙的环境问题上。做SolidWorks二次开发的绝大多数老手项目文件里还是.csproj的老格式配.NET Framework。先把这个路径走通再考虑新框架战略上最稳妥。2.2 添加引用与Copy Local设置创建好控制台项目后右键“添加引用”。可以直接通过“浏览”按钮去SolidWorks安装目录找DLL。一般路径是C:\Program Files\SolidWorks Corp\SolidWorks\在这个目录下能看到SolidWorks.Interop.sldworks.dll和SolidWorks.Interop.swconst.dll。部分地区或定制版安装路径会不一样你可以直接在文件管理器里搜索SolidWorks.Interop*。添加完这两个引用之后有两件事必须立刻做选中引用把“嵌入互操作类型”Embed Interop Types设置为False。如果不改项目编译时会把COM类型信息嵌入程序集运行阶段经常出现“无法将类型为X的对象强制转换为类型为Y”的诡异错误这是新手最容易遇到的坑之一。设置“复制本地”Copy Local为False。因为SolidWorks的程序集是按版本强签名的运行时由本机SolidWorks目录提供即可没必要也不应该复制到输出目录否则换一个SolidWorks版本就会因为版本不一致报错。另外如果你以后要开发插件还会用到SolidWorks.Interop.swpublished.dll这个只有插件工程才会引用。命令控制还需要SolidWorks.Interop.swcommands.dll。现在入门阶段先用两个就够了。下面这个表方便你对照程序集文件命名空间主要作用SolidWorks.Interop.sldworks.dllSolidWorks.Interop.sldworks核心对象接口如SldWorks、IModelDoc2、FeatureManagerSolidWorks.Interop.swconst.dllSolidWorks.Interop.swconst枚举常量如swDocPART、swDocumentTypes_eSolidWorks.Interop.swpublished.dllSolidWorks.Interop.swpublished插件开发注册相关接口SolidWorks.Interop.swcommands.dllSolidWorks.Interop.swcommands插件命令ID定义2.3 C#代码里如何引入命名空间引用配置好之后在代码文件顶部加上两行using这是每个SolidWorks C#项目的标配using SolidWorks.Interop.sldworks; using SolidWorks.Interop.swconst;前者给你SldWorks、IModelDoc2这些核心接口类型后者给你swDocPART这类枚举值。后面写代码时如果智能提示找不到类型先回头检查这两行有没有漏。3. 连接SolidWorks实例3.1 连接正在运行的SolidWorks做独立程序第一步就是“拿到”SolidWorks的Application对象。这个对象是所有API调用的根你可以把它理解为整个SolidWorks程序的控制台。如果用户已经打开了SolidWorks最直接的连接方式是用Marshal.GetActiveObjectusing System.Runtime.InteropServices; SldWorks swApp null; try { swApp (SldWorks)Marshal.GetActiveObject(SldWorks.Application); } catch (COMException) { // 没有找到正在运行的实例走创建逻辑 }这段代码的原理是SolidWorks在系统ROTRunning Object Table里注册了自己的实例标识SldWorks.ApplicationGetActiveObject会把那个对象拿回来再强转成SldWorks接口。说白了就是“去房间里喊一声王工在不在在就直接用他的工位”。3.2 创建新的SolidWorks进程如果当前没有运行中的SolidWorks或者用户想直接由程序启动一个新进程则用ProgID创建Type swType Type.GetTypeFromProgID(SldWorks.Application); SldWorks swApp (SldWorks)Activator.CreateInstance(swType); swApp.Visible true;GetTypeFromProgID会查找注册表中SolidWorks注册的COM类信息Activator.CreateInstance负责把实例创建出来。创建完成后设置Visible true否则新进程可能是隐藏的用户会一脸懵。这里有一个非常重要的细节项目编译目标平台必须和SolidWorks位数一致。新版SolidWorks都是64位程序如果你的C#项目在Visual Studio里被设置成了x86那么Activator.CreateInstance创建COM实例大概率会失败或者行为异常。解决办法很简单在“项目属性 - 生成 - 平台目标”里选择x64或者至少选“AnyCPU”时勾选“首选32位”不要勾选。这个问题在SolidWorks 2018以后的版本特别明显我见过太多人卡在这里。3.3 封装一个可复用的连接方法每次写程序都复制粘贴获取连接的代码很烦。我的习惯是把它封装成一个静态方法放到一个公共类里using System; using System.Runtime.InteropServices; using SolidWorks.Interop.sldworks; public static class SwAppHelper { public static SldWorks Connect() { SldWorks swApp null; try { swApp (SldWorks)Marshal.GetActiveObject(SldWorks.Application); } catch (COMException) { swApp null; } if (swApp null) { Type swType Type.GetTypeFromProgID(SldWorks.Application); if (swType null) throw new Exception(本机未安装SolidWorks或COM注册信息缺失。); swApp (SldWorks)Activator.CreateInstance(swType); swApp.Visible true; } return swApp; } }以后每个独立程序里直接调SwAppHelper.Connect()简单省事。为什么推荐先GetActiveObject再CreateInstance因为用户很多时候已经开着SolidWorks和模型了直接连接现有的实例能无缝操作当前文档省去自己重新打开模型文件的流程。只有连不上时才启动新实例这样的逻辑对用户最友好。3.4 关于COM对象释放的提示新手常常会在网上看到“用完后要Marshal.ReleaseComObject释放”的说法于是写了一大堆繁琐的释放代码。我个人的建议是独立程序里在程序退出前不必特意释放COM引用进程结束时系统会自动清理。如果在循环里频繁创建COM对象那确实要注意释放否则SolidWorks可能越跑越慢。但与其纠结释放不如先确保“获取”和“调用”的正确性释放的问题后面专门讲COM生命周期时再展开。尤其不要对同一个COM对象多次调用ReleaseComObject那会直接把SolidWorks内部引用计数打乱轻则抛异常重则整个SolidWorks崩溃。4. 第一个可运行示例遍历当前模型的特征树4.1 完整代码这一节我给你一个能直接跑的最小工程作用是把当前模型的特征树遍历一遍并打印到控制台。这个例子麻雀虽小涉及的ActiveDoc、FeatureManager、Feature这三个对象是后续几乎所有开发都要用的核心对象模型。using System; using System.Runtime.InteropServices; using SolidWorks.Interop.sldworks; public static class SwAppHelper { public static SldWorks Connect() { SldWorks swApp null; try { swApp (SldWorks)Marshal.GetActiveObject(SldWorks.Application); } catch (COMException) { swApp null; } if (swApp null) { Type swType Type.GetTypeFromProgID(SldWorks.Application); if (swType null) throw new Exception(本机未安装SolidWorks或COM注册信息缺失。); swApp (SldWorks)Activator.CreateInstance(swType); swApp.Visible true; } return swApp; } } class Program { static void Main(string[] args) { SldWorks swApp SwAppHelper.Connect(); IModelDoc2 doc swApp.ActiveDoc; if (doc null) { Console.WriteLine(当前没有打开任何模型。); return; } Console.WriteLine(当前文档 doc.GetTitle()); FeatureManager featMgr doc.FeatureManager; Feature feat featMgr.FirstFeature(); while (feat ! null) { PrintFeature(feat, 0); feat feat.GetNextFeature(); } Console.WriteLine(遍历完成按任意键退出。); Console.ReadKey(); } static void PrintFeature(Feature feat, int depth) { string name string.IsNullOrEmpty(feat.Name) ? 无名特征 : feat.Name; Console.WriteLine(new string( , depth * 2) name); Feature sub feat.GetFirstSubFeature(); while (sub ! null) { PrintFeature(sub, depth 1); sub sub.GetNextSubFeature(); } } }4.2 逐段解释关键逻辑这段代码看起来不长但每一部分背后都有值得讲清楚的点。swApp.ActiveDoc返回的是当前SolidWorks中处于激活状态的模型文档。注意它可能为null比如SolidWorks开了但没有任何文档或者当前激活的是浏览器窗口。所以代码里要做空判断这是每次获取文档后的标准安全检查。doc.FeatureManager是特征树管理器它提供FirstFeature()方法返回特征树里的第一个特征。然后通过GetNextFeature()沿着兄弟节点往下遍历直到返回null。GetFirstSubFeature()和GetNextSubFeature()则是向下钻取子特征用的比如一个拉伸特征下面的草图、圆角等。理解特征树结构对SolidWorks二次开发至关重要。你完全可以把Feature想象成一个树节点每个节点可能有子节点遍历方式和二叉树遍历几乎一样只是节点数量和层级不固定。这段递归打印的代码其实就是你后面做“批量修改特征名”“识别特定类型特征并改参数”的基础。4.3 运行结果长什么样假设用户在SolidWorks里打开一个简单的轴类零件特征树是轴-驱动端.sldprt ├── 凸台-拉伸1 │ └── 草图1 ├── 切除-拉伸1 │ └── 草图2 └── 圆角1运行程序后控制台会输出当前文档轴-驱动端.sldprt 凸台-拉伸1 草图1 切除-拉伸1 草图2 圆角1 遍历完成按任意键退出。注意这里的Feature.Name在某些情况下可能为空字符串比如一些虚特征、参考特征所以代码里做了空值兜底。真正常见的坑是用户想看特征的“类型”比如区分拉伸和切除光靠Name不靠谱因为用户可能给特征改成奇怪的名字。要判断特征类型应该用Feature.GetTypeName2()返回的系统类型名例如“Extrusion”“Cut”等这个后面讲到特征操作时再细说。4.4 从被动录到主动写的关键一步第一次跑通遍历特征树之后我建议你试试手动建几个特征比如改名、加材质、加注解然后再运行程序看看特征树输出有什么变化。这样能帮你快速建立起“模型结构”和“API对象模型”之间的映射关系。很多时候新手去看SolidWorks官方API帮助几百个接口绝对会迷路。我的方法是先记住一条主线SldWorks-IModelDoc2-FeatureManager-Feature先把这棵对象树爬熟了其他接口都是围绕这条主干开枝散叶的。5. 新手提升效率的速学方法宏录制配合C#5.1 为什么推荐宏录制如果你以前没接触过SolidWorks API直接让你记住每个API方法是很痛苦的。我自己刚入门时也有这个阶段记不住接口名、记不住参数顺序搜索引擎翻半天结果还是一知半解。后来我发现最快的学习方式其实就在软件里宏录制。SolidWorks自带宏录制功能在菜单“工具 - 宏 - 录制”开启然后你对模型做的操作会被自动翻译成VBA代码。录制完成后保存再在“工具 - 宏 - 编辑”里打开就能看到。这段VBA代码就是你刚才操作的API调用序列。虽然语言是VBA调用的却是完全相同的对象模型和方法名所以把它转成C#并不难。举个最简单的例子我录一个动作打开一个有活动文档的SolidWorks然后什么也不做只点一下“属性”看文档名。录出来的VBA大致是Dim swApp As Object Dim swDoc As Object Set swApp Application.SldWorks Set swDoc swApp.ActiveDoc Debug.Print swDoc.GetTitle转成C#就是SldWorks swApp SwAppHelper.Connect(); IModelDoc2 swDoc swApp.ActiveDoc; Console.WriteLine(swDoc.GetTitle());本质上Application.SldWorks对应的就是我们前面封装的Connect()方法其他对象方法和属性几乎一一对应。5.2 VBA转C#时的三个典型差异第一个差异是对象声明方式。VBA里大量使用As Object或者干脆不声明类型在运行时才知道。C#里必须写明确类型所以你要根据变量实际调用的方法去猜接口类型。这个一开始比较痛苦但反过来也是一个学习过程需要SketchManager就声明成ISketchManager需要Feature就声明成Feature。第二个差异是默认属性和可选参数。VBA允许省略一些默认属性比如Debug.Print swDoc.GetTitle里的GetTitle()括号都能省略。C#不行必须是完整的方法调用而且很多COM方法有可选参数C#里往往要通过重载或传递Type.Missing来补齐。第三个差异是枚举常量。VBA宏录制代码里会出现swDocPART这类的常量这是SolidWorks定义在swconst里的枚举。在C#里要用(int)swDocumentTypes_e.swDocPART或者直接转换成对应枚举注意类型匹配。5.3 录宏学习API的正确姿势录宏不是让你照着抄而是看它“调用了哪些对象、哪些方法”。你需要做的是把一次操作录制成VBA。通读代码标出调用了哪些对象如FeatureManager、SketchManager、SelectionManager。查API帮助或在Visual Studio里输入“对象名.”看智能提示了解这些接口还有哪些别的方法。自己动手用C#重新实现一遍同样的功能不要直接翻译粘贴。录宏最大的价值在于它是SolidWorks官方给出的“标准操作对应的API道路图”。比如你不知道怎么创建拉伸特征就手动录一个拉伸VBA代码会告诉你顺序是先建草图还是先选面调用的方法名是什么参数怎么传。这套路学会之后你的学习速度会快很多。不过录宏也不是万能的。录制出来的代码往往包含大量冗余调用比如界面刷新、视图操作之类你要学会抓主干、去枝叶。另外宏录制适合学习“做一件事的流程”但不太适合学习“如何设计一个健壮的程序”业务逻辑、异常处理、用户交互还是要靠你自己设计。6. 常见问题与排查技巧实录6.1 编译报错“命名空间不存在”这个错误最常见的原因是引用没加对或者加了但命名空间写错。检查一下项目里是否有SolidWorks.Interop.sldworks和SolidWorks.Interop.swconst这两个引用然后确认代码顶部有对应的using语句。如果引用添加正确但智能提示里还是看不到SldWorks大概率是引用了错误的DLL——注意不要引用SolidWorks.Interop.swpublished.dll来做独立程序那个是插件开发用的里面的接口命名空间和普通API不同。6.2 运行时提示“无法将类型为…的对象强制转换为类型为…”这种问题绝大多数和Embed Interop Types有关。请在引用属性里把“嵌入互操作类型”设为False再重新编译。如果这个问题是在项目迁移或别人电脑上运行时才出现还要检查目标机器的SolidWorks版本是否与开发时一致或者是否安装了多个版本的SolidWorks导致GAC里注册信息混乱。6.3 ActiveDoc一直返回null有几种常见原因。第一种是SolidWorks里确实没打开模型这好办手动打开一个零件或装配体再试。第二种是SolidWorks窗口虽然开着但当前激活的是SolidWorks的某些内部窗口比如资源管理器面板、属性页面ActiveDoc拿不到文档。这种情况你需要确保用户真的激活了一个模型窗口或者在代码里更稳妥地遍历所有打开的文档。遍历文档可以用int docCount swApp.DocumentCount(); object[] docNames (object[])swApp.GetDocumentNames();先拿到文档数量再根据文档名做处理比单纯依赖ActiveDoc更可靠。6.4 后台线程调用API崩溃SolidWorks的COM对象模型对线程非常敏感COM对象的调用必须始终在同一个Apartment线程。新手最典型的错误是开了一个Task或Thread然后在后台线程里直接调用swApp.ActiveDoc结果程序莫名其妙崩溃或卡死。我的建议是独立程序里所有SolidWorks API调用都在主线程完成。如果你的程序需要做耗时的批量处理可以先把需要的数据读取到普通C#对象里处理在后台线程做处理完再回到主线程写回SolidWorks。如果以后要用WinForms/WPF做界面还需要考虑到UI线程和COM线程的关系这一部分等讲到界面开发时再详细展开。6.5 64位环境下进程位数不匹配导致的报错前面已经强调过一次再重复一遍新版SolidWorks是64位C#项目“平台目标”必须设置为x64否则Activator.CreateInstance或者COM调用会莫名失败。判断方法也很简单在Visual Studio“项目属性 - 生成 - 平台目标”里确认一下是x64不是x86。如果你是用AnyCPU并勾选了“首选32位”那也等于x86一样会踩坑。下面这个速查表是这套入门配置下最常遇到的问题现象最常见原因解决方向编译找不到SolidWorks类型没添加引用或using写错浏览SolidWorks安装目录添加sldworks和swconst引用运行时报类型转换异常Embed Interop Types True互操作嵌入设为False后重新编译ActiveDoc为null模型未打开或激活窗口不正确确保有激活文档或用DocumentCount遍历CreateInstance失败平台位数不一致平台目标改为x64后台线程调用崩溃跨线程访问COM对象API调用收敛到主线程执行换个电脑运行报版本错误SolidWorks版本与程序集不匹配重新添加目标机器的程序集并重新编译6.6 一个容易被忽略的细节程序启动时SolidWorks已经退出了怎么办如果你的程序先启动用户中途又手动关掉了SolidWorks那么所有后续API调用都会抛COMException。独立程序要做好这个异常捕获并在捕获后提示用户重新打开SolidWorks或者让程序重新创建一个实例。我的建议是把整个业务循环包在一个try-catch里遇到COM错误时提示并退出而不是让程序默默崩溃。还有一个相关的细节如果你用CreateInstance创建了新的SolidWorks进程用户关闭SolidWorks主窗口只是隐藏程序进程可能还在后台。代码里看到“SolidWorks主窗口关了但进程没退”是常见现象这不是你的程序问题而是COM对象的生命周期和SolidWorks主窗口的显示状态并不完全绑定。对于新手先知道有这回事等以后做健壮程序时再针对性处理。结尾这个系列写第一篇时我特意把内容控制在“能跑起来”这个最小范围里。在我看来新手学SolidWorks API最大的障碍不是某个API不会用而是第一公里太长——环境没配好、引用没设对、连接不上实例任何一个环节卡住都会让人打退堂鼓。你在跟着文章跑通这个遍历特征树的程序后后续学建模、装配、工程图、属性批量处理都是在同一套对象模型上做文章。我个人在实际操作中的体会是连接和遍历特征树这套代码你最好自己新建项目敲一遍不要复制粘贴。敲的过程里你会注意到哪些方法返回的是object、哪些地方要强转、哪些调用返回null需要判断。这些感觉是看代码永远体会不到的。等到你敲熟了再回头看SolidWorks帮助文档里那些接口列表会发现它们不再是一堆枯燥的名字而是一棵你心里已经有数的树。下一篇我打算讲如何用C#批量操作自定义属性那时候遍历特征树的思路就能直接派上用场了。