简介:面向机械设计与CAD二次开发人群的SolidWorks C#开发入门模板,定位为可直接运行的插件工程骨架。资源以Visual Studio解决方案形式组织,包含Addin插件项目、WinForms与XtraForm窗体、工具栏图标、SolidWorks操作类、事件处理与属性页示例,能帮助开发者快速理解ISwCAddin接口实现、命令注册、界面嵌入和数据交换等关键流程,解决从零搭建SDK项目、调试和部署插件的常见问题。
包内共99个文件,压缩后10.24MB,以34个dll运行库和18个cs源码文件为核心,另有resx界面资源、bmp图标、xml配置、sln解决方案及exe可执行文件;dll对应SolidWorks与.NET依赖,cs为可直接阅读的插件逻辑,图标与资源配置支撑工具栏和属性页定制,目录结构清晰,适合直接参考或修改扩展。目前已有724人学习下载;该模板适用于教学演示、项目原型与企业内部工具扩展等场景,对刚接触SolidWorks二次开发或希望快速搭建C#插件框架的工程师,是一份实用的起步模板。 搞SolidWorks二次开发这几年,我最大的体会是:很多人不是不会写代码,而是不知道从哪下手。API文档翻了几百页,示例代码抄了一堆,结果一运行全是报错。后来我把自己的项目整理成了一套可复用的SolidWorks二次开发模板,把环境配置、工程结构、API封装、异常处理这些脏活累活全部固化下来。这篇文章就把这套模板的设计思路和核心实现完整拆开,给你一条能直接落地的路线。
这套模板适合谁?如果你是用C#或VB.NET做SolidWorks插件(Add-in)的开发者,或者公司内部要做设计自动化、批量处理、参数化建模,哪怕你只是刚接触二次开发想找个靠谱的起点,这篇文章都能省下你至少一周的踩坑时间。接下来我不讲空话,全部是能直接抄作业的东西。
1. 模板的整体设计思路与方案选型
1.1 为什么一定要做“模板”,而不是直接写项目
很多人接到需求就直接打开Visual Studio,新建一个类库项目,拖几个窗体,写完一编译发现SolidWorks根本不加载。问题出在哪?SolidWorks二次开发有一套固定的“壳”需要你先搭好:插件注册信息、启动入口、COM交互、界面挂载、菜单命令注册。这就像你要开一家餐厅,光会炒菜没用,你得先有营业执照、后厨设备、菜单设计。
模板的意义就是把这家餐厅的“基础设施”先建好。你拿到手以后,只需要关注自己那盘“招牌菜”——也就是具体的业务逻辑。我见过不少项目,业务代码只占20%,剩下80%全是在处理API调用、版本兼容、资源释放这些破事。模板帮你把这些公共问题一次性解决掉,后续新项目直接复制外壳,效率能提好几倍。
1.2 模板的工程结构:三层分离,各干各的活
我使用的模板不是把代码随便堆一堆,而是按职责拆成三层。第一层是UI交互层,负责菜单、工具栏图标、属性管理页(PropertyManagerPage)这些用户看得见摸得着的东西。第二层是业务逻辑层,封装具体功能,比如批量导出BOM、批量修改文件属性、参数化重建模型。第三层是核心封装层,针对SolidWorks API做二次封装,比如把ModelDoc2、Feature这些常用对象封装成易用的方法,同时管理COM对象的生命周期。
你可以把这三层理解成餐厅的“服务员、厨师、采购员”。服务员(UI层)只管接待用户,厨师(业务层)只管做菜,采购员(封装层)才知道去哪里买最新鲜的菜。三层责任清晰,改菜单(功能)不会影响后厨(API封装),排查问题的时候也能快速定位是哪里出的故障。
2. 开发环境配置与核心API细节
2.1 版本对应关系:这是二次开发第一道坎
SolidWorks二次开发和普通软件开发最大的不同是多了一个“版本对齐”问题。SolidWorks 2021对应的SDK版本、.NET Framework目标框架、Visual Studio版本,三者必须互相兼容,否则插件加载时各种莫名其妙的异常就来了。我实测下来的稳定组合是:SolidWorks 2018或以上版本 + Visual Studio 2017/2019 + .NET Framework 4.7.2。如果你用更新的VS 2022,也问题不大,但目标框架建议还是选4.7.2或4.8,太高的框架版本在客户机器上可能没装。
另一个大坑是引用文件路径。新建项目后需要添加以下核心引用:SolidWorks.Interop.sldworks.dll、SolidWorks.Interop.swconst.dll、SolidWorks.Interop.swpublished.dll,这些文件位于SolidWorks安装目录的api\redist文件夹下。不要从Windows全局程序集里找,版本容易错乱。在项目里添加引用后,把每个引用的“复制本地”属性改为False,因为运行时机器的SolidWorks会自己提供这些DLL,复制到输出目录反而可能造成版本冲突。
2.2 必须吃透的对象层级:SldWorks到Feature
SolidWorks API是树形结构,理解这个层级是开发的基础。顶部是SldWorks,它代表整个SolidWorks应用程序。通过它你才能拿到当前打开的文档ModelDoc2(可能是一个零件PartDoc、装配体AssemblyDoc,或者工程图DrawingDoc)。文档下面挂的是Feature特征、Sketch草图、Body实体等子对象。新手最常见的错误是习惯直接从全局找某个特征,或者拿到一个ModelDoc2不知道用户打开的到底是零件还是装配体,写出来的代码一运行就报错。
我在模板里封装了一个SwAppHelper静态类,专门处理这些基础操作。比如GetActiveDoc()会判断文档类型并返回强类型对象,GetFeatureByName(string name)可以从特征树顶层开始递归查找,GetMassProperty()会读取质量属性并处理单位换算。拿到这些封装方法后,业务层代码只需一两行就能实现原本十几行的功能。
2.3 COM资源释放:决定SolidWorks卡不卡的关键
SolidWorks的API底层是COM,这意味着你每调用一次API拿到一个对象,就占用了一份内存。如果循环处理几百个文件却从不释放,SolidWorks会越用越卡,最后直接崩掉。我在模板里给所有返回COM对象的方法都加上了Marshal.ReleaseComObject()处理,并且对需要长期持有的对象(比如ModelDoc2)采用finally块统一释放。不要偷懒依赖GC自动回收,.NET对COM对象的垃圾回收时有延迟,而且非托管资源不会马上释放。
这里有个我踩过的坑:某些API对象(比如Feature)释放后,如果还有其它变量引用着同一个COM对象,再次访问时会抛出COMException。所以在模板里我封装了一个ComReleaseHelper.FinalizeObject(object obj)方法,它会把传入的对象置null,并且调用GC.Collect()和GC.WaitForPendingFinalizers()确保彻底释放。虽然这些操作有性能开销,但通常只会在批量任务结束后调用一次,模拟“大扫除”的角色。
2.4 多线程问题与建模稳定性
SolidWorks API不是线程安全的,很多人想把复杂的建模操作丢到后台线程里以提高界面响应速度,然后遇到各种诡异问题:模型错乱、崩溃、或者结果每次跑都不一样。我的建议是:凡是涉及修改文档的API调用,必须回到主线程执行。模板里提供了一个SwContext.ExecuteInMainThread(delegate)方法,利用SynchronizationContext将耗时的建模逻辑排队到主线程运行,而耗时长的IO操作(比如读取外部数据库、更新进度条UI)则留在后台线程。
3. 从零搭建模板:核心实现全流程
3.1 创建Add-in项目工程骨架
在Visual Studio里新建一个“类库(.NET Framework)”项目。然后添加必要引用(上文提到的三个)。接着,创建SwAddin.cs作为插件主类:
using SolidWorks.Interop.sldworks; using SolidWorks.Interop.swconst; using SolidWorks.Interop.swpublished; using System; using System.Runtime.InteropServices; namespace SwTemplate { [ComVisible(true)] [Guid("A1B2C3D4-E5F6-7890-ABCD-EF1234567890")] public class SwAddin : ISwAddin { private SldWorks swApp = null; private int commandGroupId = -1; public bool ConnectToSW(object ThisSW, int Cookie) { swApp = (SldWorks)ThisSW; // 注册命令组、菜单、工具栏图标 bool result = RegisterCommandManager(); return true; } public bool DisconnectFromSW() { // 清理菜单和命令组 Marshal.ReleaseComObject(swApp); swApp = null; return true; } private bool RegisterCommandManager() { CommandManager cmdMgr = swApp.CommandManager; // 创建一个自定义命令组,第二个参数为命令数量 commandGroupId = cmdMgr.CreateCommandGroup2(0, "SW模板", "", "", "", -1); if (commandGroupId < 0) return false; // 注册第一个命令:“批量修改属性” bool ok = cmdMgr.AddCommandItem2( commandGroupId, "批量修改属性", "", "修改自定义属性", "myIcon.bmp", "myIcon.bmp", 0, "BatchEditProperties_Click", "BatchEditProperties_Click", -1, "", swCommandItemType_e.swMenuItem, false); return ok; } } }这段代码的核心在于ConnectToSW是SolidWorks加载插件时最先调用的入口,必须在这里完成所有初始化操作。注意Guid属性要唯一,这是插件在Windows注册表中的标识,不能和别人的重复。命令注册时的回调方法字符串(BatchEditProperties_Click)会通过反射机制绑定到同一个类下的同名方法,所以这个方法的签名必须写成public void BatchEditProperties_Click(),否则点击菜单时无响应。
3.2 命令管理器与两类界面入口:菜单和命令
SolidWorks的界面定制有两种方式:一是传统菜单(MenuItem),适合功能相对少的内部工具;二是命令选项卡(CommandTab),也就是顶部Ribbon风格的工具箱,适合功能比较多、需要分组的产品化插件。上面代码里我用的是swMenuItem,如果要加一个命令选项卡,需要在RegisterCommandManager方法里继续追加以下逻辑:
CommandTab tab = cmdMgr.AddCommandTab(Cookie, "模板工具"); CommandGroup group = cmdMgr.GetCommandGroup(commandGroupId); // 在选项卡上添加按钮 tab.AddCommandTabBox(); tab.CommandTabControl.AddGroup(group.Name);我个人建议直接上命令选项卡,因为从SolidWorks 2016之后Ribbon界面是主流,用户更容易找到你的工具入口。而且命令选项卡天然支持图标分组,视觉上更专业。
3.3 封装基础功能:读取当前模型的名称和属性
一次成功的二次开发入门,从读属性开始最稳妥。因为读操作不修改模型,风险低,还能帮你验证整个链路是否通畅。我先封装一个GetActiveModelInfo方法,它输出当前文档的完整路径和文件名称:
public string[] GetActiveModelInfo() { ModelDoc2 model = swApp.ActiveDoc; if (model == null) { return new string[] { "无", "无" }; } string path = model.GetPathName(); string name = model.GetTitle(); return new string[] { path, name }; }然后,模板里增加了读取自定义属性的通用方法,这在BOM导出、图纸格式填写中非常常用:
public string GetCustomProperty(ModelDoc2 model, string propName) { string[] names = null; object[] values = null; string[] resolvedValues = null; bool[] wasResolved = null; // 读取所有自定义属性 model.GetCustomProperties2(resolvedValues, names, values, wasResolved, swCustomInfoType_e.swCustomInfoText, "swCustomInfo"); // 遍历查找指定属性 if (names != null) { for (int i = 0; i < names.Length; i++) { if (names[i] == propName) { return values[i].ToString(); } } } return ""; }关于属性的读取,有个细节是GetCustomProperties2的第三个参数values里的多行文本属性可能带有|分隔符,如果你需要应用在工程图格式里,最好再写一个转换函数,把|替换成换行符\n。这个小问题经常导致导出的BOM格式错乱,排错很费时间。
3.4 属性批量写入的完整模板:让模板具备“生产力”
读操作验证没问题后,最值得写进模板的就是批量写入属性。拿我做过的一个实际项目来说,公司要求对几百个零件写入“材质、重量、设计者、审核日期”四个属性。如果手工填,一个人填一天;用这个模板里的函数,一分钟跑完。核心代码是:
public bool BatchWriteProperties(string folderPath, string propName, string propValue) { // 打开SolidWorks批量处理模式,关闭界面重绘 swApp.SetUserPreferenceToggle( (int)swUserPreferenceToggle_e.swInputDimValOnCreate, false); swApp.DocumentVisible(false, (int)swDocumentTypes_e.swDocPART); System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo(folderPath); int total = dir.GetFiles("*.sldprt").Length; int processed = 0; foreach (System.IO.FileInfo file in dir.GetFiles("*.sldprt")) { int errs = 0; int warns = 0; ModelDoc2 model = swApp.OpenDoc6( file.FullName, (int)swDocumentTypes_e.swDocPART, (int)swOpenDocOptions_e.swOpenDocOptions_Silent, "part", ref errs, ref warns); if (model != null) { model.DeleteCustomInfo2("swCustomInfo", propName); model.AddCustomInfo3("swCustomInfo", propName, (int)swCustomInfoType_e.swCustomInfoText, propValue); model.Save3((int)swSaveAsOptions_e.swSaveAsOptions_Silent, ref errs, ref warns); model.CloseDoc(); } processed++; } return processed == total; }这里有几个关键点。OpenDoc6 打开文件时用了静默模式(swOpenDocOptions_Silent),避免弹出一堆确认窗口。写入属性前先DeleteCustomInfo2是因为如果属性已存在再写重复名,SolidWorks会报错。最后写完后立即Save3保存,再关闭文档。还有,这个循环内部千万别加Marshal.ReleaseComObject(model)来释放每个文档对象,否则SolidWorks会不稳定。正确做法是循环结束后统一调用一次GC.Collect(),把所有临时对象一起释放。
3.5 从宏录制到自定义功能:合理的插件开发次序
我还是要提醒一句:如果你对某个API函数完全没把握,先录制宏,再翻译宏代码。宏录制器的代码能展示最原始的调用链,但宏代码是过程式的,没有工程结构。我的建议是:用宏来验证API调用对不对,用模板来组织正式代码。每次搞不清函数签名时,录制一个宏看看平台生成了什么,再把它转成模板封装的风格,这是最省力的学习方式。
比如录制“设置模型材质”的宏,会得到类似:
boolstatus = part.SetMaterialPropertyName2("swMaterial", "", "6061铝")这个方法名、参数类型一目了然。然后你可以把它封装成模板里的SetMaterial(string materialName)方法,以后再调用就不用翻文档了。
4. 常见问题与排查技巧实录
4.1 插件“无法加载”或“未注册”的排查流程
这是新手最高频的报错。首先要确认是否在SolidWorks的“插件”菜单里勾选了你的插件,如果没有,多半是注册信息没写入Windows注册表。检查方法:在Visual Studio的生成事件里加一条regasm注册命令,或者使用管理员权限运行regasm /codebase /tlb手动注册。另一个坑是插件DLL依赖的.NET版本低于当前机器运行时,需要在app.config里配置supportedRuntime。
排查顺序我觉得是:先看插件列表里有没有,再看注册表HKEY_CURRENT_USER\Software\SolidWorks\AddinsStartup里的CLSID有没有对应的Guid,最后看Windows事件查看器里有没有加载失败的异常记录。大多数情况下,这三步走完就能定位根因。比如某位同事的插件在自己机器上正常,在客户机器上加载不了,最后发现客户机器是32位Office但SolidWorks是64位,目标平台选了AnyCPU导致加载失败。模板里我给项目属性固定的x64平台,从根源上避免了这个错。
4.2 调试中断点不生效或SolidWorks进程被占用的处理
用Visual Studio调试SolidWorks插件时,第一件事就是把插件项目设置为“启动外部程序”,路径指向SLDWORKS.exe。设好之后每次按F5,VS会先启动SolidWorks并自动挂载插件,这样断点才能命中。如果你调试过程中关掉了SolidWorks但进程没退出,再次启动调试会提示“端口被占用”。对策是任务管理器把SLDWORKS.exe和SLDWORKSHelper.exe全部结束,再重试。
另一个调试小技巧是,在ConnectToSW方法里加一行System.Diagnostics.Debug.WriteLine("插件已加载:" + DateTime.Now.ToString("yyyyMMddHHmmss"))。然后下载一个DebugView工具,它能捕获输出,即使不启动VS调试也能看到插件是否成功加载。排查“SolidWorks启动时静默崩溃”特别有用,因为这类问题发生在VS的调试器启动之前,断点根本来不及生效。
4.3 每次修改代码后SolidWorks界面不更新的问题
插件DLL在SolidWorks运行时被占用,是无法重新编译的。很多新手改了代码再调试,VS报“文件正在被另一进程使用”。我一般会先彻底关闭SolidWorks,再重新编译。但如果SolidWorks已经卡死退不出来,用进程管理器强制结束就好。还有一个方法是在SolidWorks插件管理器里先取消勾选插件,关闭SolidWorks,再重新编译,这样可避免进程被占用锁死。
为了减少这种“改代码-关软件-重开”的循环,模板里我加了一个独立开发模式:把业务逻辑拆成一个不依赖SolidWorks的普通类库,核心算法(比如计算减重比例、筛选文件名)可以在纯.NET环境下测试,只有真正调用API时才需要启动SolidWorks。这样改通用逻辑就不用反复开关SW了,对于迭代开发来说效率翻倍。
4.4 常见问题速查表
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 插件在插件列表里看不到 | 注册表未写入CLSID | 使用regasm或SolidWorks Add-in向导注册 |
| 菜单命令点击无响应 | 回调方法签名不对 | 确认回调方法为public void且无参数 |
| 打开工程图就崩溃 | 插件代码操作了不支持的API | 在模板里用DrawingDoc类型判断,避免用零件方法处理工程图 |
| 批量处理几百个文件后内存暴涨 | COM对象未释放 | 使用模板的ComReleaseHelper统一清理 |
| 新版本SolidWorks升级后插件失效 | 引用的Interop版本过旧 | 重新引用新版安装目录下的SDK DLL |
| 运行时报错“无效的ActiveX实例创建” | SolidWorks版本与项目不匹配 | 检查目标框架和Interop引用版本是否对应 |
| 属性写入后无法撤销 | SolidWorks二次开发API没有撤销机制 | 操作前备份原文件或先导出原属性备份CSV |
| 批处理界面卡死无响应 | 在后台线程中执行了API调用 | 所有API操作必须回到主线程,使用SynchronizationContext调度 |
4.5 一个实用的排查案例:模板数据库连接失败
我在模板里预留了一个DbHelper数据库连接模块,目的是让模板不只是修改模型,还能把设计信息同步到企业的数据库里。有个项目部署后,客户反馈“属性写入失败”,排查发现数据库连接字符串里的服务器IP写的是测试环境的127.0.0.1,而现场部署时没有修改配置。我把数据库配置独立成一个app.config,每次在新环境部署时优先修改配置。各位如果要在模板里加类似模块,记得把配置外置,不要硬编码。
5. 从模板到完整工具:复制过去的扩展路径
到这里,核心模板的骨架已经完整了。但模板的作用是降低起步难度,最终你要做的是在模板骨架上扩展出能解决实际问题的完整工具。
比如做工艺设计,可以加几何识别模块:遍历Feature对象,识别孔特征、阵列特征、钣金折弯特征,输出加工工艺路线。做产品设计管理,可以加参数驱动模块:读取Excel表格里的设计参数,批量驱动Dimension的SystemValue,自动更新三维模型。做生产制造数据时,可以集成标准件库和焊件切割清单导出,把模板变成衔接设计与生产的数据中转站。
在扩展过程中,我特别强调一个认知:二次开发的本质是“自动化重复劳动”,但它的学习曲线很陡,最大的成本不在写代码本身,而在理解和信任SolidWorks对象模型。我见过有团队花三个月把模型批量转STEP的插件写出来,结果因为装配体里面的子零件引用路径没处理好,导出的文件在客户那里装配全散架。所以模板里我不仅封装了新文件导出,还事先遍历装配体的Component2对象把缺失的引用项打出来,在导出前就给出警告。
另外一个很值得尝试的方向是接入PDM(产品数据管理)。设计团队一旦用了PDM库,所有文件都从库中检出/检入,插件就要考虑工作流中文件的锁定状态与版本。模板里预留了一个简单的PdmHelper方案,如果你公司有PDM,可以在此基础上扩展。
最后说几句掏心窝的话
我做了多年SolidWorks二次开发,最大的感悟是:模板的意义不在于替你写功能,而在于帮你把坑提前踩平。环境对齐、COM释放、界面注册、版本兼容,这四座大山如果没有模板,每个新项目都要重新爬一遍。而现在,我只需复制模板,改业务代码,一天就能出一个能跑的原型。整理模板这几年,等于我把“开放式问题”变成了“填空题”,每次消化掉一个新API,就把心得沉淀到模板里,越用越顺手。希望这篇文章里的方案,也能让你的SolidWorks二次开发之路少走弯路。
本文还有配套的精品资源,点击获取