乐于分享
好东西不私藏

手把手教你开发VBE插件:打造属于你的VBA编辑器扩展

手把手教你开发VBE插件:打造属于你的VBA编辑器扩展
一、用Visual Studio创建一个类库(.net framework类库)项目,如下图。这里以创建的项目名称为VBEaddin为例。
二、给项目添加引用
需要额外添加3个引用:
1、通过浏览添加VBE6EXT.OLB。这个文件一般是位于:C:\Program Files (x86)\Common Files\Microsoft Shared\VBA\VBA7,或者C:\Program Files (x86)\Common Files\Microsoft Shared\VBA\VBA6
2、通过搜索框里输入extensibility找到如下图这个引用(根据VS的安装路径,每台电脑可能都不相同),勾选添加它。
3、再搜索添加System.Windows.Forms这个引用,因为后续需要用它做对话框和窗体。
三、在项目的属性中,点击“程序集信息”,找到项目的GUID编号,复制它。
修改项目自动创建的类Class1的名称,改为Connect
然后把复制得到的GUID编号写入Connect类的顶部,并给这个类取一个唯一的Id名称“MnVBEaddin.Connect”,再添加一个ComVisible的属性,如下:
using System;using System.Runtime.InteropServices;namespace VBEaddin{    [Guid("a861b430-588b-48ed-a929-86e27b0b6738")]    [ProgId("MnVBEaddin.Connect")]    [ComVisible(true)]    public class Connect    {    }}
三、编写程序代码,Connect这个类必须继承IDTExtensibility2这个类,并实现其所有方法,代码改写成下面这样:
using System;using System.Reflection;using System.Runtime.InteropServices;using Extensibility;using Microsoft.Office.Core;using Microsoft.Vbe.Interop;namespace VBEaddin{    [Guid("a861b430-588b-48ed-a929-86e27b0b6738")]    [ProgId("MnVBEaddin.Connect")]    [ComVisible(true)]    public class Connect : IDTExtensibility2    {        private VBE _vbe;        private CommandBarPopup _myMenu;        private CommandBarButton _button;        void IDTExtensibility2.OnConnection(object Application, ext_ConnectMode ConnectMode, object AddInInst, ref Array custom)        {            _vbe = (VBE)Application;            CommandBar mainMenuBar = _vbe.CommandBars[1];            _myMenu = (CommandBarPopup)mainMenuBar.Controls.Add(                MsoControlType.msoControlPopup, Missing.Value, Missing.Value, Missing.Value, true);            _myMenu.Caption = "全栈开发的码农(&M)";            _button = (CommandBarButton)_myMenu.Controls.Add(                MsoControlType.msoControlButton, Missing.Value, Missing.Value, Missing.Value, true);            _button.Caption = "Hello World(&H)";            _button.Click += (CommandBarButton Ctrl, ref bool CancelDefault) =>            {                System.Windows.Forms.MessageBox.Show("Hello World!");            };        }        void IDTExtensibility2.OnDisconnection(ext_DisconnectMode RemoveMode, ref Array custom)        {            // ✅ 清理:先删除按钮,再删除菜单            if (_button != null)            {                _button.Delete();                Marshal.ReleaseComObject(_button);                _button = null;            }            if (_myMenu != null)            {                _myMenu.Delete();                Marshal.ReleaseComObject(_myMenu);                _myMenu = null;            }        }        void IDTExtensibility2.OnAddInsUpdate(ref Array custom)        {        }        void IDTExtensibility2.OnStartupComplete(ref Array custom)        {        }        void IDTExtensibility2.OnBeginShutdown(ref Array custom)        {        }    }}
四、编译程序并注册文件
因为功能比较简单,只显示一个Hello world的提示框,所以这个插件就这样完工了,现在可以编译程序。编译后得到一个VBEaddin.dll文件。
最后,还需要将这个插件程序注册到电脑里,你可以手工注册,但是操作起来比较麻烦,为此,我写了一个注册的VB脚本,全部代码如下,把这段代码保存成*.vbs文件,放在和VBEaddin.dll相同的文件夹内,点击运行它,就完成插件程序的注册。
' 检查是否以管理员权限运行If Not WScript.Arguments.Named.Exists("elevated") Then    CreateObject("Shell.Application").ShellExecute WScript.FullName, """" & WScript.ScriptFullName & """ /elevated""""runas", 1    WScript.QuitEnd If' 主程序Dim currentDir, dllPath, regasmPath, objShell, objFSO, strCommand, intReturnDim pluginProgId, pluginName, pluginDesc, regPath32, regPath64' ========== 配置区域(请根据你的插件修改) ==========pluginProgId = "MnVBEaddin.Connect"   ' ProgId,与代码中的 [ProgId] 保持一致pluginName = "全栈码农的插件"            ' 外接程序管理器中显示的名称pluginDesc = "演示VBE插件的最简示例"            ' 描述信息' ====================================================Set objShell = CreateObject("WScript.Shell")Set objFSO = CreateObject("Scripting.FileSystemObject")' 获取当前脚本所在目录currentDir = objFSO.GetParentFolderName(WScript.ScriptFullName) & "\"dllPath = currentDir & "VBEaddin.dll"' 检查 DLL 是否存在If Not objFSO.FileExists(dllPath) Then    Call ShowError("错误:当前目录下未找到 VBEaddin.dll!")    WScript.Quit 1End If' 自动选择 RegAsm 路径(32位或64位)If objFSO.FileExists("C:\Windows\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe") Then    regasmPath = "C:\Windows\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe"ElseIf objFSO.FileExists("C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe") Then    regasmPath = "C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe"Else    Call ShowError("错误:未找到 RegAsm.exe!请确保已安装 .NET Framework 4.0 或更高版本。")    WScript.Quit 1End If' ========================================' 第一步:注册 COM 组件' ========================================objShell.Popup "正在注册 COM 组件...", 2, "提示", 64strCommand = Chr(34) & regasmPath & Chr(34) & " " & Chr(34) & dllPath & Chr(34) & " /codebase"intReturn = objShell.Run(strCommand, 0, True)If intReturn <> 0 Then    Call ShowError("COM 注册失败!请检查错误信息。")    WScript.Quit 1End IfobjShell.Popup "COM 组件注册成功!", 1, "成功", 64' ========================================' 第二步:添加 VBE 插件注册表项(32位和64位)' ========================================' 32位 VBE 注册表路径regPath32 = "HKCU\Software\Microsoft\VBA\VBE\6.0\Addins\" & pluginProgId' 64位 VBE 注册表路径(64位 Office 需要)regPath64 = "HKCU\Software\Microsoft\VBA\VBE\6.0\Addins64\" & pluginProgIdOn Error Resume Next' 创建32位注册表项Call AddVBERegistryEntry(regPath32)If Err.Number <> 0 Then    Call ShowError("无法创建32位 VBE 注册表项:" & Err.Description)    WScript.Quit 1End If' 创建64位注册表项Call AddVBERegistryEntry(regPath64)If Err.Number <> 0 Then    Call ShowError("无法创建64位 VBE 注册表项:" & Err.Description)    WScript.Quit 1End If' 检查是否所有写入都成功If Err.Number = 0 Then    objShell.Popup "VBE 插件注册表项添加成功!" & vbCrLf & _                   "32位路径:" & vbCrLf & regPath32 & vbCrLf & vbCrLf & _                   "64位路径:" & vbCrLf & regPath64, 5, "成功", 64Else    Call ShowError("VBE 注册表项写入失败:" & Err.Description)    WScript.Quit 1End IfOn Error GoTo 0' ========================================' 完成提示' ========================================Dim finalMsgfinalMsg = "插件安装成功!" & vbCrLf & vbCrLf & _           "请按以下步骤验证:" & vbCrLf & _           "1. 重新启动 Excel" & vbCrLf & _           "2. 按 Alt+F11 打开 VBA 编辑器" & vbCrLf & _           "3. 点击【工具】→【外接程序管理器】" & vbCrLf & _           "4. 找到 """ & pluginName & """,确认已勾选"objShell.Popup finalMsg, 5, "安装完成", 64' ========================================' 子函数:添加 VBE 注册表项' ========================================Sub AddVBERegistryEntry(regPath)    ' 创建插件注册表项    objShell.RegWrite regPath, "", "REG_SZ"    ' 写入 FriendlyName    objShell.RegWrite regPath & "\FriendlyName", pluginName, "REG_SZ"    ' 写入 Description    objShell.RegWrite regPath & "\Description", pluginDesc, "REG_SZ"    ' 写入 LoadBehavior(3 = 启动时加载并已加载)    objShell.RegWrite regPath & "\LoadBehavior", 3, "REG_DWORD"End Sub' 显示错误信息的函数Sub ShowError(message)    Dim errorCmd    errorCmd = "PowerShell -Command Write-Host '" & message & "' -ForegroundColor Red"    objShell.Run errorCmd, 0, True    objShell.Popup message, 0, "错误", 16End Sub 
注册后,打开Excel或solidworks,进入“开发工具”或编辑宏,启动VBE,就发现VBE的主菜单多了一个菜单项,点击它就会弹出信息框。
最后,为了卸载这个插件,也同样写一个VB脚本来快速卸载,卸载脚本的全部代码如下:
' UninstallVBEAddin.vbs' 卸载 VBEaddin 插件(COM注册 + VBE注册表项)' 检查是否以管理员权限运行If Not WScript.Arguments.Named.Exists("elevated") Then    CreateObject("Shell.Application").ShellExecute WScript.FullName, """" & WScript.ScriptFullName & """ /elevated", "", "runas", 1    WScript.QuitEnd If========== 配置区域(与安装时保持一致) ==========pluginProgId = "MnVBEaddin.Connect"   ' ProgId=================================================Dim currentDir, dllPath, regasmPath, objShell, objFSO, strCommand, intReturnDim regPath32, regPath64, confirmSet objShell = CreateObject("WScript.Shell")Set objFSO = CreateObject("Scripting.FileSystemObject")' 获取当前脚本所在目录currentDir = objFSO.GetParentFolderName(WScript.ScriptFullName) & "\"dllPath = currentDir & "VBEaddin.dll"' 确认卸载confirm = objShell.Popup("确定要卸载 VBEaddin 插件吗?" & vbCrLf & vbCrLf & _                         "此操作将:" & vbCrLf & _                         "1. 从 COM 注册表中注销插件 DLL" & vbCrLf & _                         "2. 从 VBE 外接程序管理器中移除插件(32位和64位)", _                         0, "确认卸载", vbYesNo + vbQuestion)If confirm <> vbYes Then    WScript.QuitEnd If' 自动选择 RegAsm 路径If objFSO.FileExists("C:\Windows\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe") Then    regasmPath = "C:\Windows\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe"ElseIf objFSO.FileExists("C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe") Then    regasmPath = "C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe"Else    Call ShowError("错误:未找到 RegAsm.exe!")    WScript.Quit 1End If========================================' 第一步:卸载 COM 组件========================================If objFSO.FileExists(dllPath) Then    objShell.Popup "正在卸载 COM 组件...", 1, "提示", 64    strCommand = Chr(34& regasmPath & Chr(34& " " & Chr(34& dllPath & Chr(34& " /u"    intReturn = objShell.Run(strCommand, 0True)End If' ========================================' 第二步:删除 VBE 插件注册表项(32位和64位)' ========================================regPath32 = "HKCU\Software\Microsoft\VBA\VBE\6.0\Addins\" & pluginProgIdregPath64 = "HKCU\Software\Microsoft\VBA\VBE\6.0\Addins64\" & pluginProgIdOn Error Resume Next' 删除32位注册表项(使用 DeleteKey 方法删除整个项)Call DeleteVBERegistryEntry(regPath32, "32位注册表项")' 删除64位注册表项Call DeleteVBERegistryEntry(regPath64, "64位注册表项")On Error GoTo 0========================================' 完成提示========================================objShell.Popup "插件卸载完成!请重新启动 Excel 后生效。", 3, "卸载完成", 64' ========================================' 子函数:删除整个 VBE 注册表项' ========================================Sub DeleteVBERegistryEntry(regPath, pathName)    Dim psCmd    ' 使用 PowerShell 的 Remove-Item 删除整个注册表项(递归删除)    psCmd = "PowerShell -Command ""Remove-Item -Path 'HKCU:\Software\Microsoft\VBA\VBE\6.0\Addins\" & pluginProgId & "' -Recurse -Force -ErrorAction SilentlyContinue"""    objShell.Run psCmd, 0True    psCmd = "PowerShell -Command ""Remove-Item -Path 'HKCU:\Software\Microsoft\VBA\VBE\6.0\Addins64\" & pluginProgId & "' -Recurse -Force -ErrorAction SilentlyContinue"""    objShell.Run psCmd, 0TrueEnd Sub' 显示错误信息的函数Sub ShowError(message)    Dim errorCmd    errorCmd = "PowerShell -Command Write-Host '" & message & "' -ForegroundColor Red"    objShell.Run errorCmd, 0, True    objShell.Popup message, 0, "错误", 16End Sub