SolidWorks二次开发:从VBA宏到DLL插件的固定框架设计

发布时间:2026/9/8 12:58:58
SolidWorks二次开发:从VBA宏到DLL插件的固定框架设计 1. VBA宏越写越乱为什么我们一定要一个“固定框架”先说个场景。用过SolidWorks宏的人应该都有这种体验录制的宏代码又长又丑一堆SelectByID2、GetType这种东西命名混乱、流程冗长。刚开始图省事在宏里直接改参数、加逻辑前几版还行但当你的宏开始涉及多文档操作、装配体遍历、批量导出、界面交互时代码就会迅速失控。我见过不少同行VBA宏写到一两千行之后基本就处于“改一处、崩三处”的状态。全局变量满天飞宏与宏之间靠Sub过程名互相跳转根本没有“对象”的概念。最典型的问题是你在A零件上跑没问题换到B零件上就开始报错前几天还能用的宏今天打开SolidWorks就提示“运行时错误91”或者干脆卡死。这时候你会想能不能把宏封装成DLL插件答案是可以而且这是正规做SW二次开发的基本路子。但“封装成DLL”不是简单的复制粘贴、换个后缀名它需要一套固定的工程结构——也就是我这篇要讲的“插件固定框架”。这一节的定位适合已经写过一阵子SW VBA宏、对SolidWorks API有一定基础、但是还没系统接触过DLL插件开发的读者。如果你打算把维护成本压下来让宏变成真正的插件这套固定框架就是你绕不开的地基。“固定框架”这四个字听起来像是多此一举但实际上它就是用来解决两件事的代码结构的稳定性不管插件界面怎么变框架核心不轻易动生命周期管理的可靠性特别是SolidWorks进程和插件之间的交互不能再靠VBA的“碰运气”式运行。后者是DLL插件和VBA宏最大的差异点。VBA宏是SolidWorks帮你启动了运行环境跑完就丢DLL插件是SolidWorks外部加载进来的一个COM组件它的生命周期、资源释放、错误处理都得自己管。所以我在这篇第二节里不打算直接甩一堆API代码而是先把框架的“固定”部分讲清楚。为什么固定因为这部分代码每个插件都用得上你写好一次后面所有插件开发都能复用。2. 框架的地基类模块、SW App对象生命周期和事件要说清楚固定框架先得过一道坎VBA里的模块和DLL类库到底差在哪儿。2.1 VBA模块是“过程集合”类模块是“对象工厂”VBA里最常见的模块是标准模块Module里面是一堆Sub和Function。这种结构下数据靠全局变量传递过程之间是平铺的调用关系。你可以理解成一个工具箱锤子、扳手、螺丝刀全扔在一个抽屉里用的时候翻。而类模块Class Module不同。它定义的是“一类东西”的行为你通过New来创建实例实例有自己的数据、方法、属性。比如你可以做一个“零部件处理类”每个实例代表一个最终要处理的零件里面有材料、数量、路径这些属性有导出操作、改名操作这些方法。DLL插件本质上是把这一套类结构编译成COM组件。也就是说VBA宏里的类模块是你进入DLL世界的第一级台阶。2.2 SW App对象的获取与生命周期SolidWorks二次开发第一行代码永远是获取SolidWorks应用对象。VBA宏里你用Application就行但在DLL里你通常要通过ROTRunning Object Table或者通过ConnectToSW来拿对象。固定框架里我习惯做一个专门的管理类叫SWAppManager负责获取当前运行的SolidWorks实例持有SldWorks对象引用在插件加载和卸载时进行初始化和清理。听起来简单但这里有个坑如果你在DLL里随便GetObject拿到的是一个“新的”COM引用有时候会拉起一个新的SolidWorks进程或者拿到已经失效的实例。这个必须有稳定的获取逻辑。 伪代码示意获取当前SW实例 Public Function GetCurrentSW() As SldWorks.SldWorks Dim swApp As SldWorks.SldWorks 通过运行对象表获取避免启动新实例 具体用 Marshal.GetActiveObject 或 Rot 枚举 附带校验如果没有实例就提示用户 End Function再一个关键点是生命周期。DLL里如果持有SW对象引用而不释放SolidWorks关闭时插件会拖住进程导致SW关不掉、DLL文件被占用无法更新。框架里必须在Disconnect方法里把引用置空规范释放。2.3 事件钩子让插件变得“主动”VBA宏是“你点运行它跑一遍”。DLL插件可以反过来挂在SolidWorks里监听文档打开、保存、选择变化、重建模型这些事件。固定框架里需要一组事件处理逻辑。最常见是DocumentOpen、DocumentClose、ActiveModelDocChange这些。这些事件的挂接要在初始化时用AddHandler或者直接实现ISwAddin接口。实现事件后插件就能在用户打开零件时自动执行批处理而不用手动点宏。2.4 我建议固定的“三件套”结构一个插件工程里我固定分成三个部分部分职责类比入口类AddIn类实现插件接口负责加载/卸载酒店前台客人入住退房走这里业务类功能类具体的功能逻辑比如批量改名、导出PDF客房服务员干实际活儿工具类Utilities通用方法日志、文件路径、SW对象获取后勤仓库谁都能来领工具这三个部分稳定下来你后面每个插件的开发都只是往“业务类”里添功能入口和工具很少动。这才是“固定框架”的意义。3. 把VBA逻辑搬进固定框架一个能跑的示例骨架理论讲完得来实际的了。我这里的示例环境是SolidWorks Visual Studio VB.NET做DLL插件VB.NET是最容易从VBA过渡的选择。如果你用C#概念完全一样只是语法区别。这一节里我直接给你一个最简可运行的代码骨架然后告诉你每一段都干了什么、为什么这么干。3.1 定义插件入口类ComVisible(True) Guid(你的GUID-1) Public Class SwAddIn Implements SwAddin Dim swApp As SldWorks.SldWorks Dim cookie As Integer Public Function ConnectToSW(ByVal ThisSW As Object, ByVal Cookie As Integer) As Boolean Implements SwAddin.ConnectToSW swApp DirectCast(ThisSW, SldWorks.SldWorks) cookie Cookie 初始化菜单/命令 挂接事件 Return True End Function Public Function DisconnectFromSW() As Boolean Implements SwAddin.DisconnectFromSW 清理事件 释放引用 swApp Nothing Return True End Function End Class这段代码是整个插件的壳。ConnectToSW是SolidWorks调用你的第一站签名的两个参数不能改ThisSW是当前SW实例Cookie是你这个插件实例的标识后续注册命令、切换命令管理状态都要用它。你原来VBA宏里的Sub main()到DLL时代基本就没有意义了。宏的入口是一次性执行插件入口是加载时执行一次然后插件就常驻在内存里等菜单命令来触发业务。3.2 命令管理类给插件做“操作菜单”有了入口类接下来要让用户能调用你的插件功能。最稳妥的方式是使用CommandManager在SW菜单栏或者工具栏添加按钮点击后触发对应的业务方法。Public Class CommandManagerHelper Private swApp As SldWorks.SldWorks Private cookie As Integer Public Sub AddCommandGroup() Dim cmdMgr As CommandManager swApp.CommandManager 创建命令组 添加命令按钮绑定回调方法 End Sub End Class这里有个细节命令按钮的回调是通过CommandTab和CommandItem的事件来触发的。你需要实现ICommandGroup相关的接口或者使用CommandManager的AddCommandGroup方法后再通过CommandGroup的CommandClick事件来响应点击。很多初学者卡在这一步是因为CommandManager的API层级比较绕CommandManager下面有CommandGroupCommandGroup下面有CommandItem还要区分“分组里的按钮”和“暂停状态”。固定框架里我会把“创建命令组”“添加命令”“绑定事件”三个动作封装成一个通用方法传入命令名、图标路径、回调方法即可。3.3 事件管理器把宏的“轮询”变成“订阅”VBA宏经常要主动遍历模型来获取状态但DLL插件可以反过来等SW通知你。Public Class SwEventManager Private WithEvents swApp As SldWorks.SldWorks Public Sub Attach(ByVal app As SldWorks.SldWorks) swApp app End Sub Private Sub swApp_DocumentOpen(ByVal Doc As Object) Handles swApp.DocumentOpen 文档打开时自动执行逻辑 End Sub Private Sub swApp_ActiveModelDocChange() Handles swApp.ActiveModelDocChange 切换文档时自动更新UI状态 End Sub End Class这里要提醒一句WithEvents是VB.NET里很省事的写法但如果你在运行时反复Attach多个实例事件可能重复绑定造成某个操作被触发两三次。框架里必须保证同一个SW对象只绑定一次事件。3.4 工具类把VBA里常用函数统一搬进来VBA宏里人人都写过一些通用函数获取当前文档、获取选中的面、数组转字符串、写日志。这些函数在DLL插件里也要用但它们属于“谁都要用”的东西应当放在独立的Utilities类里而不是写在某个业务类下面。Public Class SwUtilities Public Shared Function GetCurrentDoc(swApp As SldWorks.SldWorks) As ModelDoc2 Return swApp.ActiveDoc End Function Public Shared Sub LogMessage(msg As String) 写日志便于调试和排查 End Sub End Class我见过有人把这些通用函数放在插件入口类里结果入口类越写越长最后千行起步可读性极差。固定框架的意义就是把这些东西分摊清楚。4. 从VBA到DLL的必经之路编译、注册和版本混乱框架写好了你面临的下一个问题是把它变成SolidWorks认得的DLL。这一步没有经验的用户能卡一整天。各种dll修复工具、dll冲突、无法找到宏或宏被禁用、error: flash download failed - target dll has been cancelled这类搜出来的东西都是南辕北辙的。4.1 工程属性COM可见性和强名称DLL插件是COM组件第一件事是确保你的类库“COM可见”。工程属性里勾选“Make assembly COM-visible”给每个公开类添加ComVisible(True)特性生成时设置Register for COM interop开发调试阶段勾选发布时手写注册命令。还需要一个强名称签名Strong Name Key否则无法注册到全局程序集缓存或者在某些环境下加载失败。这是.NET类库的老规矩了。有人为了省事跳过这一步装到别的机器上发现DLL根本不加载回头排查半天就是这个原因。4.2 注册DLLx64和x86要注意SolidWorks分32位和64位版本。现在绝大多数是64位那么注册工具要用64位的regasm.exe。以管理员身份运行命令提示符执行C:\Windows\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe 你的插件.dll /codebase可以加/tlb生成类型库文件。这一步的目的是把.NET程序集的信息写进注册表SolidWorks启动时才找得到这个插件。不加/codebase的话如果你的DLL不在GAC里它根本定位不到文件位置。你可能会遇到这种诡异情况在开发机上一注册就能用换一台机器怎么注册都不行。多数时候不是代码的问题而是你的DLL引用了SW API的Interop程序集目标机器没装对应版本的SolidWorks API或者.NET Framework版本不够。排查的时候打开注册表看一下HKCR\CLSID下有没有你DLL的GUID再检查InprocServer32项指向的路径是不是存在。4.3 SolidWorks加载不到插件的排查顺序固定框架已经很稳定了但DLL加载失败经常是环境问题。我个人的排查顺序是否以管理员身份运行SW和注册工具注册表里GUID是否存在、路径是否正确是否启用了SolidWorks的“插件”开关事件日志里有没有.NET Runtime异常用fuslogvw程序集绑定日志查看器打开绑定日志看是哪个依赖加载失败。有次我在客户机器上排查一个DLL注册好但SW里不显示的案例最后发现是RegAsm用错了版本客户的SolidWorks是64位的但软件装的是32位Office结果随手用了Framework目录下的32位RegAsm注册表写到了Wow6432Node下面SolidWorks当然找不到。所以框架文档里必须写明你的目标SolidWorks是多少位就用对应位数目录下的RegAsm。4.4 “宏被禁用”和“VBA宏不可用”的误区搜热词里看到有人搜“word无法找到宏或宏被禁用”“vba宏的免费启用方法”这说明很多刚上手的人还在跟VBA宏环境做斗争。DLL插件跟宏不同的是它不依赖VBA编辑器是否启用也不依赖宏安全性设置。DLL插件注册成功SolidWorks的加载项列表里就会出现你的插件名称勾选即可。如果你的DLL插件在加载项列表里出现但勾选后没反应多半是ConnectToSW里抛了异常。此时别急着改注册表先在ConnectToSW里写一个日志文件所有关键步骤打点Public Function ConnectToSW(...) As Boolean ... Try Log(开始连接) swApp DirectCast(...) Log(获取SW对象成功) ... Log(初始化完成) Return True Catch ex As Exception Log(连接失败: ex.ToString()) Return False End Try End Function有了日志你至少能区分是对象获取失败、命令注册失败还是事件挂接失败。很多人一看到加载失败就重装SW那真的是白费力气。5. 往框架里加业务功能三处要动的地方框架建好之后你很可能要做的是“把我的旧宏塞进去”。注意是“把功能塞进去”不是“把代码复制进去”。VBA宏里大量依赖全局变量、递归引用、动态类型这些东西直接搬进类库会出很多兼容性问题。5.1 把“宏主流程”变“类方法”VBA宏通常是这样Sub main() Dim swApp As SldWorks.SldWorks Set swApp Application.SldWorks Dim swModel As SldWorks.ModelDoc2 Set swModel swApp.ActiveDoc 200行逻辑... End SubDLL插件里这段逻辑应该拆成几个方法比如Public Class BatchExportFeature Public Sub Run(swApp As SldWorks.SldWorks) Dim doc swApp.ActiveDoc ValidateDoc(doc) CollectData(doc) DoExport(doc) End Sub Private Sub ValidateDoc(doc As ModelDoc2) 检查文档类型、打开状态 End Sub Private Sub CollectData(doc As ModelDoc2) 收集自定义属性、特征树信息 End Sub Private Sub DoExport(doc As ModelDoc2) 执行导出 End Sub End Class这样拆有几个好处第一每个方法可以独立调试第二错误发生时能定位到具体步骤第三以后要给同一个功能加一个“导出Excel”的选项只要改DoExport或增加一个分支。5.2 处理“运行时要用户选择”宏里经常弹窗让用户输入参数比如批量导出时问用户“导出到哪个文件夹”。VBA里直接InputBox搞定。但DLL插件挂在SolidWorks里时弹窗会抢焦点频繁弹窗体验很差。固定框架的建议是用Taskpane侧边窗格或用户窗体集中收集参数一次输入多次使用。我自己的框架里准备了一个简单的Parameters类专门保存用户最近一次输入减少重复劳动。5.3 错误处理的等级划分插件长期挂在SolidWorks里一个错误没处理好可能导致整个SW崩溃。框架里的错误处理必须有分级策略错误级别处理方式示例可忽略记日志继续执行某个零件没有自定义属性跳过可回退弹提示终止本次操作选择的文档类型不支持严重清理资源提示重启SW内存溢出、API崩溃级异常不要把所有的Error都MsgBox弹给用户那样用户会烦死。也不要把所有异常都吃掉否则插件出问题你都不知道哪里错了。固定的日志模块是必须的。5.4 框架与CommandManager按钮的关系每个新功能都要加一个命令按钮吗不一定。固定框架建议你用“一个按钮多个子动作”的交互比如“五金工具”这个按钮下面有个下拉菜单里面是“批量改名”“批量导出PDF”“装配体重命名”这些具体功能。这样既减少菜单栏占用也让用户觉得插件是个完整的工具集而不是散装的一堆按钮。CommandManager支持创建带下拉菜单的CommandGroup具体做法是给每个CommandItem指定Tooltip和Callback再设好菜单类型。5.5 状态管理与“灰显”好的插件要能感知当前文档状态。比如用户没打开任何文档时“导出PDF”按钮应该是灰色的点不动。这个功能需要监听ActiveModelDocChange事件在事件回调里调用CommandGroup的EnableCommandItem方法。Private Sub UpdateUIState() Dim hasDoc As Boolean (swApp.ActiveDoc IsNot Nothing) 根据状态启用/禁用命令 End Sub这套逻辑跟你的业务功能是解耦的所以放在框架层很合适。我每次都建议新手把UI状态更新当成框架一部分而不是业务功能的一部分否则你会发现在每个业务方法里都要写一遍状态判断重复代码极多。6. 避坑实录我踩过的框架级错误最后这个部分分享几个我在从VBA宏转向DLL插件框架时踩过的坑。没有这些坑你可能整个框架都写完了但一到实际使用才发现基础地方就埋了雷。6.1 全局变量和静态变量的幽灵VBA宏里全局变量用顺手了编译DLL插件后容易延续这个坏习惯在类模块里用Shared静态变量保存状态。这个坑很隐蔽因为插件加载后是常驻的静态变量的生命周期跟着SW进程走不是你每次运行宏那样的“跑完就清空”。结果是第一次运行功能一切正常第二次运行发现上一次的数据还在第三次逻辑开始变得诡异。所以我强烈建议框架内的状态数据要么跟着具体实例走要么在每次业务执行开始时重新初始化。6.2 SW对象引用未释放导致的DLL文件占用开发阶段你肯定要频繁改代码、重新编译DLL。这时候最讨厌的事情是编译的时候提示DLL文件被占用无法覆盖。原因就是SolidWorks还保持着你的插件DLL引用或者你代码里持有SW对象没有释放导致进程没完全松开。解决办法是在DisconnectFromSW里面把所有事件去掉、把所有对象引用置空。如果你做了这些还是无法覆盖DLL就关掉SolidWorks再编译。注不要开着SolidWorks编译后直接跑很多情况下你加载的还是旧版本代码。6.3 引用不同版本的SolidWorks Interop程序集SolidWorks API版本更新很频繁2020、2021、2022、2023、2024的Interop程序集不完全一样。你开发用的版本和客户机器上的版本不一致就会出现运行时找不到方法或属性的问题。框架里我一般锁定一个目标版本比如2019版API然后尽量只用最稳定的接口。如果必须用新版API那么就在目标环境里加上版本判断否则插件在不同机器上表现不一致排查成本极高。6.4 小心“宏录制”代码的坐标系与选择依赖VBA宏录制出来的代码百分之百依赖用户界面选择比如选了一个面、选了一条边代码里全是SelectByID2带一串长长的特征名。这类代码放到插件里最不稳定因为用户的操作路径和录制时不一样特征名一变就找不到对象。框架里提供的业务方法应该优先用对象遍历、特征树检索、名称规则等方式来找目标而不是依赖用户预先选中了什么。这是一个设计思维的转变。6.5 插件卸载不完全越更新越乱开发过程中你会反复卸载、重装插件。SolidWorks插件管理器其实有记忆功能如果你的卸载逻辑里没清理干净旧版本的CLSID还残留在注册表加载新版本时就会冲突。表现为插件列表里有两个同名入口或者勾选一个加载了另一个。框架里的卸载逻辑建议包含删除命令组、注销事件、释放引用并且提供一个“卸载脚本”专门移除注册表项。开发阶段省下的这一步会在后续带来成倍的麻烦。7. 固定框架的版本演进和复用策略当你的第一个框架跑通以后接下来就是持续复用的问题。我建议你把自己写的这套固定框架单独保存成一个工程模板每次新插件项目直接从模板复制而不是每次都从空白工程开始。7.1 工程模板里应该包含什么一个可复用的工程模板至少包含插件入口类Connect/Disconnect骨架命令管理器类支持多命令组事件管理器类常用SW事件占位工具类日志、路径、SW对象获取一个示例业务类演示如何被命令调用注册/卸载脚本RegAsm命令。复制模板后你只需要改GUID、类名、功能逻辑就行。这样每个新插件的一二十分钟就能把壳搭好剩下时间全部花在业务上。7.2 版本兼容性管理跨SolidWorks版本部署插件是很多外部插件开发者的痛点。固定框架里边最好设计一个Compatibility类专门检查当前SW版本号并且在需要的API调用前后做版本判断。Public Shared Function GetSWVersion(swApp As SldWorks.SldWorks) As Integer 返回主版本号比如 30 代表 SW202231 代表 SW2023 End Function实际上SW版本号和年份的对应关系以及API差异处理每个大版本都有一些细微差别这个需要你自己整理。7.3 日志系统是框架的仪表盘我在框架里最后沉淀下来的是一个轻量级日志模块。它会把每一次插件加载、退出、命令执行、功能错误都记录到一个txt文件里。这些日志在开发调试时帮了大忙尤其是客户报告的“偶发问题”没有日志根本无从排查。日志的格式不要太花哨一行一条时间、级别、消息。示例2025-12-01 10:15:22 INFO 插件已连接SW版本30 2025-12-01 10:16:05 INFO 命令执行导出PDF 2025-12-01 10:20:11 ERROR 导出PDF失败未找到文档别小看这个日志它能回答用户问你“为什么刚才那一下没反应”时的所有疑问。8. 下一节预习和练习建议第二节的内容到这里算是把框架的“形”讲完了。不过框架这种东西光看不练是体会不到它的价值的我强烈建议你拿几个真实的VBA宏照着上面的固定框架思路亲手重构一遍再编译成DLL。几个可以立刻动手的练习把你最常用的一个宏拆成入口类、业务类、工具类三层结构给这个宏功能加上一个SW菜单命令按钮挂接文档打开事件让插件在打开特定类型的零件时自动执行某个操作。这一套做下来你对框架的理解会比看十篇教程都管用。写这套固定框架你觉得最难受的地方就是在VBA里从未考虑过“生命周期”和“状态管理”而一进入DLL它们就成了核心问题。只要把这两个问题解决你的插件就已经能稳定运作了。下一篇我会重点讲CommandManager的命令管理细节和Taskpane界面设计把这些UI层的东西补齐框架才能真正算完整。先写到这里我去调试自己的一个导出插件了等第三节出来再会。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询