实际开发中,新建空的类库项目后,无任何可运行的插件逻辑,第一步就是编写这个固定框架——这是SW识别并加载插件的基础,几乎所有自定义功能(如特征重命名、监听器)都基于这个框架开发
先看示例代码:
这份代码是可运行模板,插件能被识别、加载,且能正常在 SolidWorks 【工具】菜单下显示「我的插件」→「运行测试」按钮,后续只需要改中间的RunTest和自定义方法即可:
Imports SolidWorks.Interop.sldworks
Imports SolidWorks.Interop.swconst
Imports SolidWorks.Interop.swpublished
Imports System.Runtime.InteropServices
Imports System.Windows.Forms
' 【固定】类注解,保留原样(Guid/ProgId可保留你的值)
<ComVisible(True)>
<Guid("81D0DA48-8B9A-4BC2-A235-5575E509484D")>
<ProgId("MySolidWorksAddin.SwAddin")>
Public Class SwAddin
Implements ISwAddin
' 【固定】核心私有变量,保留原样
Private swApp As SldWorks
Private swCookie As Integer
Private commandGroup As ICommandGroup
' 【固定】插件加载入口,仅保留CreateMenuByCommandGroup调用,其余不改
Public Function ConnectToSW(ByVal thisSW As Object, ByVal cookie As Integer) As Boolean _
Implements ISwAddin.ConnectToSW
Try
swApp = TryCast(thisSW, SldWorks)
swCookie = cookie
If swApp IsNot Nothing Then
swApp.SetAddinCallbackInfo2(0, Me, cookie)
CreateMenuByCommandGroup() ' 菜单创建调用,保留
swApp.SendMsgToUser("插件加载成功!【工具】菜单可找到我的插件")
Return True
End If
Catch ex As Exception
MessageBox.Show($"插件连接失败: {ex.Message}")
End Try
Return False
End Function
' 【固定】插件卸载入口,仅保留RemoveMenuByCommandGroup调用,其余不改
Public Function DisconnectFromSW() As Boolean _
Implements ISwAddin.DisconnectFromSW
Try
RemoveMenuByCommandGroup() ' 菜单清理调用,保留
swApp = Nothing
commandGroup = Nothing
Return True
Catch ex As Exception
MessageBox.Show($"插件卸载失败: {ex.Message}")
Return False
End Try
End Function
' 【可改】菜单创建逻辑(修复后,能正常生成菜单/按钮,可改菜单/按钮名称)
Private Sub CreateMenuByCommandGroup()
Try
' 增加CommandManager空值判断,解决「找不到CommandManager」问题
If swApp.CommandManager Is Nothing Then
swApp.SendMsgToUser("CommandManager 初始化成功,正在创建菜单…")
Return
End If
Dim cmdMgr As CommandManager = swApp.CommandManager
Dim cmdGroupID As Integer = 999 ' 用大数字避免与SolidWorks内置命令组冲突,更稳定
commandGroup = cmdMgr.GetCommandGroup(cmdGroupID)
If commandGroup Is Nothing Then
' 可改:菜单/按钮名称、提示文字
Dim menuName As String = "运行测试"
Dim menuTip As String = "点击执行我的自定义功能"
' 创建命令组(插件主菜单名称:我的插件,可改)
commandGroup = cmdMgr.AddCommandGroup2(cmdGroupID, "我的插件", "我的SolidWorks自定义插件", "插件说明", -1, True)
' 核心:添加按钮,最后一个参数传0(图标+文字),无枚举无报错
commandGroup.AddCommandItem2(menuName, -1, menuTip, menuTip, 0, "RunTest", "", swCookie, 0)
commandGroup.ShowInMenu = True ' 强制显示到【工具】菜单
End If
Catch ex As Exception
swApp.SendMsgToUser($"菜单创建失败: {ex.Message}")
End Try
End Sub
' 【可改】菜单清理逻辑,与创建逻辑对应,保留即可
Private Sub RemoveMenuByCommandGroup()
Try
If commandGroup IsNot Nothing Then
commandGroup.Delete()
End If
Catch
End Try
End Sub
' ==============================================
' 【重点替换区】菜单回调函数(按钮点击触发,改成你的功能入口)
' ==============================================
Public Sub RunTest()
Try
' 这里写「按钮点击后要执行的核心逻辑」,替换成你的代码即可
' 示例:判断是否打开零件文档(可保留,也可删掉)
If swApp.ActiveDoc Is Nothing Then
swApp.SendMsgToUser("请先打开一个SolidWorks文档(零件/装配/工程图)!")
Return
End If
' 调用你的自定义功能方法(下面的MyCustomFunction)
MyCustomFunction()
Catch ex As Exception
swApp.SendMsgToUser($"功能执行失败: {ex.Message}")
End Try
End Sub
' ==============================================
' 【重点替换区】自定义功能方法(你的核心业务逻辑,全部写在这里)
' ==============================================
Private Sub MyCustomFunction()
' ********** 这里删掉示例代码,替换成你自己的代码 **********
Dim modelDoc As IModelDoc2 = swApp.ActiveDoc
swApp.SendMsgToUser($"当前文档:{modelDoc.GetTitle()},开始执行我的自定义功能!")
' 你的SolidWorks操作代码(建模、草图、参数修改等)写在这里
' **********************************************************
End Sub
' 【固定】注册函数,保留原样
<ComRegisterFunction()>
Public Shared Sub RegisterFunction(ByVal t As Type)
Try
Dim key = Microsoft.Win32.Registry.LocalMachine.CreateSubKey(
"SOFTWARE\\SolidWorks\\Addins\\" & "{" & t.GUID.ToString() & "}")
key.SetValue(Nothing, 0)
key.SetValue("Title", "我的插件") ' 可改:SolidWorks插件列表显示的名称
key.SetValue("Description", "我的SolidWorks自定义插件") ' 可改:插件描述
key.SetValue("LoadAtStartup", 1) ' 1=开机自启,0=手动加载
key.Close()
Catch ex As Exception
MessageBox.Show($"插件注册失败: {ex.Message}")
End Try
End Sub
' 【固定】反注册函数,保留原样
<ComUnregisterFunction()>
Public Shared Sub UnregisterFunction(ByVal t As Type)
Try
Microsoft.Win32.Registry.LocalMachine.DeleteSubKey(
"SOFTWARE\\SolidWorks\\Addins\\" & "{" & t.GUID.ToString() & "}", False)
Catch
End Try
End Sub
End Class
👉 本节实操步骤框架:1. 生成插件唯一GUID(身份证);2. 定义插件主类并实现ISwAddin接口;3. 声明插件核心私有变量;4. 编写插件加载(ConnectToSW)和卸载(DisconnectFromSW)方法;5. 编写插件注册/反注册函数(实现注册表自动录入/删除);6. 编写菜单按钮创建方法(实现插件界面入口)。
插件框架是SolidWorks和Windows强制要求的“规矩”,核心部分不能改。下面逐部分拆解,告诉你每段代码的“底层在干吗”,并补充GUID生成这一关键操作。
2.1 类顶部的3个“身份证”注解(补充GUID生成要点)
<ComVisible(True)>
<Guid("81D0DA48-8B9A-4BC2-A235-5575E509484D")>
<ProgId("MySolidWorksAddin.SwAddin")>
Public Class SwAddin
Implements ISwAddin
End Class
底层逻辑拆解
-
<ComVisible(True)>:告诉Windows“我是一个能被调用的COM组件”,相当于外包员工“愿意接受公司调配”。设为False的话,SW根本看不到你的插件。
-
<Guid("唯一ID")>:全局唯一标识符,相当于外包员工的“身份证号”——全世界不会重复,SW和Windows通过这个ID唯一识别你的插件。一个插件,一个GUID,永不更改,即使更新插件版本也尽量不变,否则需清理旧注册表条目。
-
<ProgId("插件别名")>:COM组件的“昵称”,比GUID好记,SW也能通过这个昵称找到插件,方便开发时引用。
-
Implements ISwAddin:必须实现这个接口,相当于外包员工“签了工作协议”——ISwAddin接口强制要求写两个方法(ConnectToSW=入职,DisconnectFromSW=离职),少一个都不行。
补充:GUID生成实操步骤
GUID的唯一性直接影响插件注册,手动编写极易重复,实际开发中生成GUID的核心操作如下:
在VS中,点击顶部菜单栏「工具」→「创建GUID」;
在弹出的窗口中,选择「注册表格式」(形如 {81D0DA48-8B9A-4BC2-A235-5575E509484D}),点击「复制」;
回到代码中,将复制的GUID粘贴到 <Guid("")> 的引号内,删除多余空格,确保格式正确;
保存代码,后续无论插件如何迭代,除非完全重构,否则不要修改此GUID。
2.2 3个核心私有变量(外包的“工作工具”)
Private swApp As SldWorks
Private swCookie As Integer
Private commandGroup As ICommandGroup
底层逻辑拆解
-
swApp As SldWorks:SolidWorks应用程序的“实例句柄”,相当于外包员工拿到了公司的“总机电话”——所有操作(建模、弹窗、监听事件)都要通过这个变量调用SW的API,没有它,插件就是“没嘴的哑巴”,没法和SW沟通。
-
swCookie As Integer:插件的“会话ID”,相当于公司给外包员工分配的“工牌编号”——SW同时加载多个插件时,靠这个编号识别“哪个插件在和我说话”,避免混淆。
-
commandGroup As ICommandGroup:SW命令组的引用,相当于外包员工“工位上的工牌”——保存你创建的菜单/按钮资源,卸载插件时要通过它收回工牌,否则SW界面会残留无效按钮(就像外包走了,工牌还挂在工位上)。
2.3 ConnectToSW:插件“入职报到”(加载入口,强调核心调用)
Public Function ConnectToSW(ByVal thisSW As Object, ByVal cookie As Integer) As Boolean _
Implements ISwAddin.ConnectToSW
Try
swApp = TryCast(thisSW, SldWorks) ' 拿到SW的“总机电话”
swCookie = cookie ' 记下自己的“工牌编号”
If swApp IsNot Nothing Then
' 核心调用:必须执行此方法,否则SW无法传递指令给插件
swApp.SetAddinCallbackInfo2(0, Me, cookie)
CreateMenuByCommandGroup() ' 申请“工位”(创建菜单按钮)
Return True ' 入职成功
End If
Catch ex As Exception
MessageBox.Show($"插件连接失败: {ex.Message}")
End Try
Return False ' 入职失败
End Function
底层逻辑拆解(关键步骤,补充核心调用说明)
SW启动后,扫描注册表找到你的插件,会先创建插件类(SwAddin)的实例,相当于“通知外包员工来入职”;
SW调用ConnectToSW方法,传入两个参数:thisSW是SW自身的应用实例(相当于公司把总机电话交给你),cookie是分配的会话ID(工牌编号);
swApp = TryCast(thisSW, SldWorks):把SW给的“通用电话”转为“专属电话”(强类型转换),确保能调用SW的所有API;
swApp.SetAddinCallbackInfo2:极端重要,缺一不可!相当于外包员工“向公司报备”——告诉SW“我已到岗,后续有工作(菜单点击、事件触发)请找我”,不执行此方法,SW无法把指令传给你的插件,菜单点击必无反应;
CreateMenuByCommandGroup:向SW申请“在界面上显示我的按钮”(创建菜单),相当于外包员工“认领工位”;
返回True/False:告诉SW“我入职成功/失败”,返回False的话,SW会直接放弃加载插件。
2.4 DisconnectFromSW:插件“离职交接”(卸载入口)
Public Function DisconnectFromSW() As Boolean _
Implements ISwAddin.DisconnectFromSW
Try
RemoveMenuByCommandGroup() ' 归还“工位”(删除菜单)
' 释放资源:告诉Windows“我不用这些工具了,可回收”
swApp = Nothing
commandGroup = Nothing
Return True ' 交接完成
Catch ex As Exception
MessageBox.Show($"插件卸载失败: {ex.Message}")
Return False
End Try
End Function
底层逻辑拆解
触发时机:SW关闭、用户在“插件列表”取消勾选你的插件、或通过API卸载插件时,会调用这个方法(相当于外包员工离职)。
关键作用:
-
RemoveMenuByCommandGroup:删除创建的菜单/按钮,相当于“归还工位”,避免SW界面残留无效按钮;
-
swApp = Nothing/commandGroup = Nothing:释放对SW对象的引用——COM组件的垃圾回收机制需要手动触发,不释放的话,会导致SW内存泄漏(资源被占用,越用越卡),严重时SW直接崩溃。
2.5 注册/反注册函数:插件“录入/删除通讯录”
<ComRegisterFunction()>
Public Shared Sub RegisterFunction(ByVal t As Type)
Try
' 注册表路径改为CurrentUser,无需管理员权限,提升部署兼容性(微软推荐COM注册方式)
Dim key = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(
"SOFTWARE\\SolidWorks\\Addins\\" & "{" & t.GUID.ToString() & "}")
key.SetValue(Nothing, 0) ' 标记为有效插件
key.SetValue("Title", "我的插件") ' SW插件列表中显示的名称
key.SetValue("Description", "SolidWorks自定义插件") ' 描述
key.SetValue("LoadAtStartup", 1) ' 1=SW启动时自动加载(不是电脑开机)
key.Close()
Catch ex As Exception
MessageBox.Show($"插件注册失败: {ex.Message}
提示:当前采用CurrentUser路径注册,无需管理员权限,若仍失败请检查路径是否存在。")
End Try
End Sub
<ComUnregisterFunction()>
Public Shared Sub UnregisterFunction(ByVal t As Type)
Try
' 对应CurrentUser路径删除注册表条目
Microsoft.Win32.Registry.CurrentUser.DeleteSubKey(
"SOFTWARE\\SolidWorks\\Addins\\" & "{" & t.GUID.ToString() & "}", False)
Catch
End Try
End Sub
底层逻辑拆解
-
注册函数:不是SW调用的,是「regasm.exe」工具调用的(后续部署会讲)。相当于把你的插件信息“录入SW的人才库”,SW启动时会扫描这个路径,有条目才会显示在插件列表中。
-
LoadAtStartup=1:设置为1表示SW启动时自动加载插件(相当于把外包员工设为“常驻岗”),设为0则需要用户手动在插件列表勾选。
-
必须是Shared(静态)方法:因为注册时插件还没实例化(外包员工还没入职),只能调用“静态的入职登记表”,没法调用实例方法。
-
反注册函数:执行regasm /unregister时调用,删除注册表条目(相当于把外包员工从人才库移除),卸载插件时必须执行,否则SW会一直显示这个插件(即使已经删除DLL)。
2.6 菜单创建:插件“认领工位”(界面入口)
Private Sub CreateMenuByCommandGroup()
Try
If swApp.CommandManager Is Nothing Then Return ' 避免SW未初始化完成
Dim cmdMgr As CommandManager = swApp.CommandManager ' 拿到SW的“工位管理处”
Dim cmdGroupID As Integer = 999 ' 命令组ID(自定义,确保唯一,比如用自己的生日)
commandGroup = cmdMgr.GetCommandGroup(cmdGroupID) ' 检查是否已认领过工位
If commandGroup Is Nothing Then ' 没认领过,新建工位
' 创建命令组(相当于申请一个“工位文件夹”)
commandGroup = cmdMgr.AddCommandGroup2(cmdGroupID, "我的插件", "插件说明", "", -1, True)
' 添加菜单按钮(相当于在工位上放自己的工牌),绑定回调函数RunTest
commandGroup.AddCommandItem2("切换监听器", -1, "开启/关闭事件监听", "开启/关闭事件监听", 0, "RunTest", "", swCookie, 0)
commandGroup.ShowInMenu = True ' 让工位显示在SW菜单中(默认在【工具】菜单下)
End If
Catch ex As Exception
swApp.SendMsgToUser($"菜单创建失败: {ex.Message}")
End Try
End Sub
底层逻辑拆解
-
CommandManager:SW的“界面命令管理处”,所有菜单、按钮、工具栏都必须通过它创建,不能直接修改SW的界面文件(相当于外包员工不能自己随便选工位,必须找管理处申请)。
-
cmdGroupID:命令组ID必须唯一,若两个插件用同一个ID,会导致菜单显示异常(相当于两个外包员工抢同一个工位),建议用自己的生日、GUID后几位等唯一数字。
-
AddCommandItem2的第6个参数:这是核心!填写回调函数名(比如RunTest),SW点击这个菜单按钮时,会自动调用插件中这个名称的Public Sub方法(无参数),相当于“点击工牌召唤外包员工干活”。
-
检查commandGroup Is Nothing:避免重复创建菜单(同一个ID的命令组只能创建一次),否则SW会报错(相当于外包员工不能重复认领同一个工位)。


