
简介一套基于VB.NET的VSTO Excel工具箱完整源码面向希望在Excel中扩展自定义功能的.NET开发者既能作为VSTO入门学习材料也可直接参考其数据库交互与图表绘制思路。压缩包共120个文件、约22.49MB主要以41个vb源码文件为核心配合18个resx与19个resources资源文件、10个dll依赖库及9个xml配置文件还包括sln、vbproj工程文件便于还原项目并理清资源嵌入和引用关系。源码覆盖SQL Server连接、远程数据操作、异步委托、基于GDI的甘特图绘制等典型场景从连接字符串、数据读取器到异步回调均有体现关键代码附有注释适合在真实Excel插件开发中对照调试和二次扩展。目前已有2754人学习下载对想深入掌握VSTO与VB.NET的开发者来说是一份具有直接参考价值的实战样本。1. VSTO 开发 Excel 工具箱VB.NET 版源码该怎么读、怎么写很多做 Excel 二次开发的人是从 VBA 开始的录制宏、拼字符串、到处弹 MsgBox功能倒是能跑但代码越改越怕动。VSTOVisual Studio Tools for Office把这套逻辑搬进 Visual Studio用 VB.NET 或 C# 写程序集再以加载项形式挂进 Excel 进程VSTO 开发 Excel 工具箱源码VB.NET 版这类包就是把批量改名、合并拆分、报表清洗这些常用能力封装成带功能区按钮和任务窗格的工具箱。它解决的核心问题不是“某个 Excel 功能怎么写”而是“怎么把一堆宏变成可持续维护的插件工程”。适合理顺 Excel 对象模型、准备从 VBA 换轨的开发者也适合被宏工程维护成本逼到换轨的团队。VB.NET 的语法习惯比 C# 更接近 VBA读这种源码包的路径也更顺。2. 搭 VSTO 开发环境先让 VB.NET 外接程序在 Excel 里跑起来VSTO 虽然是 Visual Studio 的一部分但默认安装不会把 Office 开发组件一起带上。在安装器的工作负载页里能找到“Office/SharePoint 开发”它包含 VSTO 项目模板、对 Office 主互操作程序集PIA的引用解析以及调试时把程序集注入 Excel.exe 进程的宿主机制。漏掉这一项新建项目列表里永远看不到 VSTO 模板只装一个“.NET 桌面开发”是远远不够的。还需要提前确认 Excel 的位数。VSTO 程序集是加载进 Excel.exe 进程里的程序集目标平台必须和 Excel 位数一致32 位 Office 配 x8664 位 Office 配 x64。项目属性里默认可能是“任何 CPU”这在 VSTO 里是最容易踩的坑编译能过F5 一启动就报“无法加载”。确认方法很简单打开 Excel文件 → 账户 → 关于 Excel版本号后面标了“64 位”就是 64 位。2.1 开发前要装齐的组件运行库、工作负载与 Excel 位数下面这张表是我在给团队搭环境时常用的核对清单缺哪一项就在对应的错误信息里找答案。组件推荐版本用途常见误配Visual Studio2022 或 2019 社区版编辑、编译、调试 VSTO 项目只装了“ASP.NET 和 Web 开发”没有 Office 工作负载.NET Framework4.7.2 到 4.8VB.NET 编译目标与 PIA 绑定目标框架高于本机运行库安装后启动即报错VSTO Runtime最新版通常随 Office 安装外接程序在 Office 内的宿主运行时Office 是精简安装运行时缺失导致加载项不出现Excel2016 及以上桌面版宿主程序使用 Microsoft Store 版 ExcelVSTO 项目无法调试注意VSTO 项目的目标框架不要随手改成 .NET 5/6/7/8官方只支持 .NET Framework。想用现代 .NET 写 Excel 插件得走 XLL 方案第 5 章会提到。2.2 新建 Excel VSTO 外接程序项目并用 F5 验证加载在 Visual Studio 里按 CtrlQ 搜索“VSTO”选择“Excel VSTO 外接程序”语言切到 Visual Basic。模板生成后解决方案里最核心的文件是 ThisAddIn.vb这就是整个工具箱的生命周期入口。注意不要选“Excel VSTO 工作簿”模板那个模板会生成 ThisWorkbook、Sheet1、Sheet2 这一套绑定具体文件的对象适合做单文件处理不适合做全局工具箱。创建完项目不要急着写功能先按 F5 验证加载链路。第一次调试时 Visual Studio 会启动一个新的 Excel.exe 实例并把调试清单注入进去。如果 Excel 已经开着会提示你关闭现有实例。F5 后 Excel 里看不到任何界面变化是正常的因为此时还没有功能区能看到新增了 Excel 加载项说明宿主通道已经打通。想确认 Office 开发工作负载是不是真的装上了可以用 vswhere 来查这是 Visual Studio 自带的安装信息查询工具$vswhere ${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe if (Test-Path $vswhere) { $vswhere -latest -products * -requires Microsoft.VisualStudio.Workload.Office -property installationPath }这段命令中-requires后面的Microsoft.VisualStudio.Workload.Office是 Office/SharePoint 开发工作负载的 ID能输出 installationPath 就说明已经装好输出为空则是缺少该负载打开 Visual Studio Installer 补上即可。用 vswhere 的另一个好处是它不依赖 PowerShell 模块任何一台装了 VS 的机器都能直接跑。2.3 项目文件里的关键节点目标框架、平台目标与 VSTO 标记VSTO 项目文件是传统非 SDK 风格的项目文件右键项目 → 卸载项目 → 编辑 vbproj能看到几个决定命运的节点。第一个是TargetFrameworkVersion一般在v4.7.2到v4.8第二个是 Release 配置下的PlatformTarget强烈建议显式写成 x64 或 x86不要留 Any CPU第三个是ProjectExtensions里的 VSTO 标记向导用它识别这是 Office 项目。PropertyGroup Condition$(Configuration)|$(Platform) Release|x64 OutputPathbin\Release\/OutputPath DefineConstantsTRACE/DefineConstants PlatformTargetx64/PlatformTarget UseVSHostingProcessfalse/UseVSHostingProcess /PropertyGroup ProjectExtensions VisualStudio FlavorProperties GUID{BAA0C2D2-18E2-41B9-852F-F413020CAA33} ProjectProperties HostNameExcel HostPackage{20A848B8-E01F-48a1-884B-19A0C0F5E253} /ProjectProperties /FlavorProperties /VisualStudio /ProjectExtensions上面这两个 GUID 是 VSTO 宿主包标识分别标记这是一个 Office 外接程序项目以及宿主动词是 Excel。初学改项目文件时最容易犯的错是在文本编辑器里动手“清理无用节点”把这段ProjectExtensions删了。删掉之后 Visual Studio 还能打开项目但已经不认识这是 VSTO 项目调试时不会再启动 Excel而是把它当普通类库跑。遇到这种问题不要重装 VS把项目文件里这段补回去就行。3. 功能区与 ThisAddIn把 VB.NET 工具箱按钮挂到 Excel 界面上工具箱给人的第一印象是功能区上出现一排按钮。VSTO 里做功能区有两条路可视化设计器和 Ribbon XML。可视化设计器像 WinForms 一样拖控件适合界面简单、只有三五个按钮的小工具但项目里拖出来的设计器会生成一大段难以评审的代码团队协作时 diff 几乎没法看。我一般选择 Ribbon XML它是一份独立的 customUI 描述文件所有布局、回调方法、图标都写在明面上接手源码的人打开 XML 就能知道工具箱有哪些入口。两者选型不是“哪个更好”而是“你的源码想怎么维护”。如果你手里这份 VB.NET 源码包用的是可视化设计器接手后先在设计器里看按钮集合如果是 XML搜索 customUI 节点就能定位全部按钮。对面向内网分发、按钮数量会持续增长的场景XML 是更省心的长期方案。3.1 功能区实现选型可视化设计器还是 Ribbon XML下面这张表是我在评估一个 VSTO 源码时最先做的对比直接决定后续代码组织方式。维度可视化设计器Ribbon XML上手成本低拖拽即可中需要懂 customUI 的节点结构自定义图标只能选内置图标可用 imageMso也支持 getImage 回调版本管理生成大量嵌套代码改动难追踪纯文本diff 清晰适合评审动态显隐写法别扭getVisible / getEnabled 回调天然支持扩展性按钮多了设计器会卡无论多少个按钮XML 都保持同样复杂度选 Ribbon XML 时项目里会有一份 Ribbon1.xml 和对应的 Ribbon1.vb。XML 文件里声明按钮与分组VB 文件里写回调方法。最简单的一组按钮如下?xml version1.0 encodingUTF-8? customUI xmlnshttp://schemas.microsoft.com/office/2009/07/customui onLoadRibbon_Load ribbon tabs tab idtabToolbox labelVSTO 工具箱 group idgrpMerge label单元格处理 button idbtnMerge label合并选中区域 sizelarge imageMsoMergeCells onActionOnMergeSelection / button idbtnSplit label拆分为行 sizelarge imageMsoTableSplit onActionOnSplitToRows / /group /tab /tabs /ribbon /customUItab是新增的一个功能区页group是页里的分组button是具体按钮。imageMso用的是 Office 内置图标名称MergeCells 和 TableSplit 都是真实存在的内置命令图标onAction对应 Ribbon1.vb 里的公开方法名。XML 里声明的每一个 id 必须在当前文档内唯一否则 Excel 会拒绝加载整条功能区。3.2 ThisAddIn 生命周期工具箱最不应该忽略的入口ThisAddIn.vb 是 VSTO 的宿主入口ThisAddIn_Startup在程序集加载成功、Excel.Application 对象可用之后触发具体时机在第一个工作簿打开之前ThisAddIn_Shutdown在 Excel 开始关闭时触发。拿这个时机做事件绑定点最合适在 Startup 里挂上工作簿打开、工作表变更事件在 Shutdown 里按顺序摘掉。Imports Excel Microsoft.Office.Interop.Excel Public Class ThisAddIn Private WithEvents ExcelApp As Excel.Application Private Sub ThisAddIn_Startup() Handles Me.Startup ExcelApp Application AddHandler ExcelApp.WorkbookOpen, AddressOf ExcelApp_WorkbookOpen End Sub Private Sub ExcelApp_WorkbookOpen(ByVal Wb As Excel.Workbook) System.Diagnostics.Debug.WriteLine(打开: Wb.FullName) End Sub Private Sub ThisAddIn_Shutdown() Handles Me.Shutdown If ExcelApp IsNot Nothing Then RemoveHandler ExcelApp.WorkbookOpen, AddressOf ExcelApp_WorkbookOpen ExcelApp Nothing End If End Sub End Class这段代码里Application是 ThisAddIn 的属性它返回当前宿主 Excel 实例AddressOf把事件委托传给 AddHandler事件回调里用Wb.FullName拿工作簿完整路径。Shutdown 阶段不要再去读Application.ActiveWorkbook此时宿主已经开始关闭很多属性拿不到值日志也未必能写成功。Startup 里也尽量不要弹窗会阻塞 Excel 的启动流程给用户“崩了”的错觉。3.3 给按钮写第一个可运行的 VB.NET 处理函数功能区按钮的回调方法统一接收两个参数control是当前按钮对象可以用来判断是哪个按钮触发的方法体里通过Globals.ThisAddIn.Application拿到 Excel 应用。以下代码是合并选中区域并保留左上角值的处理函数很多源码包里这类函数会被封装成独立 Module按钮只做转发。Imports Excel Microsoft.Office.Interop.Excel Public Class ToolboxRibbon Public Sub OnMergeSelection(control As Microsoft.Office.Core.IRibbonControl) Dim app As Excel.Application Globals.ThisAddIn.Application Dim rng As Excel.Range TryCast(app.Selection, Excel.Range) If rng Is Nothing Then System.Windows.Forms.MessageBox.Show(请先选中单元格区域) Return End If Dim cellValue As Object rng.Cells(1, 1).value2 rng.Merge() rng.Cells(1, 1).value2 cellValue End Sub End ClassTryCast是关键Excel 的Selection可能是 Range也可能是 Shape、ChartObject、SmartArt不能想当然转成 Range。合并前先用Cells(1, 1)取出首个单元格值是因为Range.Merge合并后只保留左上角的值其他位置会被清空。属性名value2而不是Value是为了避免 Excel 把日期格式转成字符串批量处理报表时这个问题很容易踩。另外要注意不要在回调里手动调用Marshal.ReleaseComObject(rng)。VSTO 运行时会统一管理 COM 对象回收提前释放可能破坏 Excel 内部引用计数导致莫名崩溃。源码里如果看到大量手写 ReleaseComObject 的代码可以直接怀疑作者的 COM 资源管理思路有问题。3.4 任务窗格把常用工具收进一个面板功能区按钮适合“点一下执行一个动作”但工具箱里还有一类操作需要输入参数比如“指定拆分列、指定输出目录”。这时常用做法是任务窗格在 Excel 右侧停靠一个面板上面放 TextBox、ComboBox 和按钮。VSTO 里用 ThisAddIn 的CustomTaskPanes集合来创建。Private WithEvents ToolboxPane As Microsoft.Office.Tools.CustomTaskPane Private Sub ShowToolboxPane() If ToolboxPane Is Nothing Then Dim panel As New ToolboxPanel() ToolboxPanel 继承 UserControl ToolboxPane Me.CustomTaskPanes.Add(panel, VSTO 工具箱) ToolboxPane.DockPosition Microsoft.Office.Core.MsoCTPDockPosition.msoCTPDockPositionRight ToolboxPane.Width 360 End If ToolboxPane.Visible Not ToolboxPane.Visible End SubCustomTaskPanes是 ThisAddIn 的实例属性不是静态集合不要尝试跨 Excel 进程共享任务窗格里的 UserControl 在构造函数里不要访问 Excel 对象模型因为控件创建时宿主还没完全就绪。DockPosition支持右侧、左侧、顶部和底部建议默认右侧Width只在右侧和左侧停靠时生效。任务窗格适合放“把选中区域整理成一维表”“按批次拆分文件”这类需要多次调整参数的入口。4. 发布与注册ClickOnce 签名、信任链和加载项注册表开发环境跑通只是第一步把 VB.NET 工具箱源码真正分发给同事用才是另一个战场。VSTO 的发布方式基本就是两种ClickOnce 和 Windows Installer。ClickOnce 是 VSTO 项目属性里自带的发布机制发布后会生成一个安装清单.vsto 文件和 publish.htm 入口双击安装后每次启动 Excel 会自动检查更新。Windows Installer 则需要单独做 MSI 封装适合公司要求通过软件中心统一管控的场景。4.1 ClickOnce 与 Windows Installer 怎么选维度ClickOnceWindows InstallerMSI发布成本低项目属性直接配置高需要额外做打包工程更新方式自动检测并更新需要重新安装 MSI 包权限要求加载项清单需要受信任签名安装时需要管理员权限内网适用性共享目录 证书即可适合组策略分发和软件资产盘点卸载控制面板可卸载标准 MSI 卸载可写清理脚本如果只是给同事们用我一般选 ClickOnce发布到一个内网共享目录每个人在浏览器里点一次 publish.htm完成后就不用管了。后续发布新版本只要在 VS 里把版本号递增并再次发布同事们下次启动 Excel 时会自动从同一 URL 拉取更新。4.2 用 PowerShell 生成自签名证书并导出给客户端ClickOnce 要求程序集和清单都经过 Authenticode 签名。没有公司证书服务器时常见做法是在自己机器上生成一个自签名代码签名证书然后把它导入到客户端机器的“受信任的根证书颁发机构”。证书信任是最容易忽略的一环发布清单签名了但客户端不信任你的证书安装时会提示“发布者未知”加载项在 Excel 里根本不会启动。PowerShell 里可以用一条命令生成内网用的代码签名证书New-SelfSignedCertificate -Type CodeSigningCert -Subject CNVSTO Toolbox Dev, OInternalTools -CertStoreLocation Cert:\CurrentUser\My-Type CodeSigningCert表示这是一个代码签名证书不是 SSL 证书-CertStoreLocation指定放到当前用户的个人证书库ClickOnce 发布时会从那里读取签名证书。生成后需要把公钥导出成 .cer 文件给客户端导入信任Get-ChildItem Cert:\CurrentUser\My\ | Where-Object { $_.Subject -like *VSTO Toolbox Dev* -and $_.NotAfter -gt (Get-Date) } | Export-Certificate -FilePath $env:USERPROFILE\Desktop\vstotoolbox.cer导出前加NotAfter判断是为了避免同时存在多个同名证书时挑到已经过期的旧证书。自签名证书没有时间戳吊销问题但过期前必须重新发布并重新导入这一步骤在交接文档里要写清楚否则半年后方方面面都会看到“签名过期”。4.3 发布路径与安装后的注册表检查发布时在项目属性 → 发布 → 发布文件夹中填共享路径比如\\fileserver\apps\ExcelToolbox安装 URL 可以留空或者填同一路径。VS 发布完成后会生成 publish.htm、.vsto 清单和一堆应用文件。客户端从资源管理器或浏览器打开该目录下的 publish.htm点击“安装”即可。安装完后Excel 加载项不会出现在“COM 加载项”里才是问题VSTO 的注册信息写在当前用户的注册表下可以用 PowerShell 快速核对$addinsPath HKCU:\Software\Microsoft\Office\Excel\Addins if (Test-Path $addinsPath) { Get-ChildItem $addinsPath | ForEach-Object { $props Get-ItemProperty $_.PSPath [PSCustomObject]{ AddinName $_.PSChildName FriendlyName $props.FriendlyName LoadBehavior $props.LoadBehavior } } | Format-Table -AutoSize }FriendlyName是发布时项目属性里填写的名称LoadBehavior决定加载项的启动方式。正常情况下 ClickOnce 安装后的值是 3表示启动时加载如果这里显示 0 或 2说明加载项曾被禁用或者初始化失败。这个检查脚本比打开 Excel 一个个看 COM 加载项快得多适合发给不在同一办公地点的同事远程排查。4.4 更新策略与本地安装兜底ClickOnce 的更新设置位于项目属性 → 发布 → 更新可以把检查频率设为“每次启动前”这样工具箱的修复和新增功能能在下一次打开 Excel 时自动生效。每次发布前手动递增发布版本号是关键版本号不变时 ClickOnce 会把新包当成同版本不会触发更新。更新失败时Excel 启动后工具箱入口可能还在但点击按钮会弹“自定义程序集未找到”此时检查客户端%LocalAppData%\Temp\VSTO目录下是否残留了旧版本缓存删除对应子目录后重新打开 Excel 即可。5. 加载失败排查把 LoadBehavior、证书和事件日志一次看全5.1 第一步先看 COM 加载项与 LoadBehaviorVSTO 加载项在 Excel 的“COM 加载项”列表里可见。打开 Excel → 文件 → 选项 → 加载项 → 管理 → COM 加载项 → 转到找到工具箱项。如果该项存在但勾选是灰的说明加载项注册存在但初始化失败如果列表里根本没有工具箱说明发布或安装环节没成功。这时转去注册表批量查 LoadBehavior。LoadBehavior含义典型场景0已禁用Excel 检测到加载项崩溃后自动禁用1已注册未加载手动修改过注册表或首次安装未启动2启动时加载可能被覆盖少见异常退出后的中间状态3启动时加载正常状态16首次运行刚安装还未完成注册正常会变成 3Excel 对反复崩溃的加载项有自动禁用机制表现形式就是 LoadBehavior 从 3 被改成 0。不要只改注册表不找原因否则下一次崩溃会再次被打回 0。真正的原因要到事件日志里找。5.2 第二步看事件查看器与 VSTO 临时目录Windows 事件查看器 → Windows 日志 → 应用程序筛选来源“VSTO”。加载失败时这里会记录异常类型、Stack Trace 和加载项名称。常见的错误模式有三种System.IO.FileNotFoundException说明程序集引用缺失检查第三方依赖有没有 Copy LocalSystem.Security.SecurityException说明证书信任链没搭好System.Runtime.InteropServices.COMException多半是 Excel 对象模型调用时机不对。临时目录%LocalAppData%\Temp\VSTO里会留下 ClickOnce 部署过程的操作日志和错误页文件名通常带时间戳直接按修改时间倒序看最新的 .html 文件。这个目录对解决“安装时什么都不提示但就是加载不了”这类问题很有价值。5.3 排完 VSTO 再看 XLL换轨方案的边界如果排错发现 VSTO 这条路在公司环境里走不通比如客户端有 Mac 版 Excel、Office 网页版或者公司明令禁止依赖 .NET Framework那就得考虑换轨到 XLL 方案。常见做法是用 ExcelDna 这类库把 VB.NET 或 C# 程序集包装成 .xll 插件注册方式和 VSTO 完全不同不依赖 ClickOnce 清单信任机制。XLL 的更新通常靠文件替换不需要写注册表分发逻辑更接近传统软件。它做 UDF自定义函数很方便但做复杂的任务窗格和功能区交互没有 VSTO 顺手替换前要确认工具箱的核心功能是“数据处理”还是“界面交互”。如果 Excel 启动后你还是看不到入口先把 COM 加载项里的勾选去掉再勾上触发一次从 0 到 3 的重新注册是最快的验证手段。本文还有配套的精品资源点击获取