欢迎光临
我们一直在努力

SW vba宏封装dll插件实战指南第二节、插件固定框架

实际开发中,新建空的类库项目后,无任何可运行的插件逻辑,第一步就是编写这个固定框架——这是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会报错(相当于外包员工不能重复认领同一个工位)。

     

    赞(0)
    未经允许不得转载:171主机测评 » SW vba宏封装dll插件实战指南第二节、插件固定框架
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址