简介:这套资源是一份基于VB.NET的VSTO二维码生成完整源码工程,面向希望为Office应用(Excel、Word等)增加二维码生成能力的.NET开发者。资源以二维码生成器项目为主体,核心覆盖ZXing.Net二维码库的集成与调用,演示了如何将二维码生成功能嵌入VSTO加载项中;通过工程内多个VB模块和配置文件,可以清楚了解加载项入口、功能触发、图片输出等环节的代码组织方式。压缩包共113个文件,整合了sln/vbproj/vb等工程与源码文件、zxing.dll等35个依赖运行库、xml/resx等配置资源,以及pfx签名证书、vsto部署清单等发布所需元素,整包大小约15.89MB。该资源已有587人学习下载,对于正在学习VSTO插件开发或需要快速搭建二维码功能模块的开发者,是一份具备直接参考价值的实践范例,通过现有VB源码可快速理清加载项配置、资源管理、部署签名等关键环节,并进行二次扩展。
1. VSTO 二维码生成,到底解决什么问题
在 Excel 里做报表、在 Word 里做单据、在 Outlook 里做邮件签名,凡是需要“在 Office 文档内部直接生成二维码”的场景,VSTO(Visual Studio Tools for Office)是目前最主流、最可控的技术路线。比起打开网页在线生成再截图贴回文档,VSTO 方案能做到数据不变、格式不丢、位置不跑,而且整个过程在 Office 进程内完成,不需要安装额外的独立程序。
这个标题的核心价值在于:用 VB(VB.NET)写 VSTO 插件,在 Office 里调用二维码生成库,把编码逻辑封装成可复用的源码。适合三类人:一是做 ERP 报表导出、需要给每行数据配二维码的桌面开发工程师;二是将 Office 文档作为业务载体、需要自动化生成的实施人员;三是想了解 VSTO 与第三方图形库如何协作的 .NET 开发者。读懂这篇,你就能从零搭出一个能跑、能调、能部署的 Word/Excel 二维码插件。
2. VSTO 插件里做二维码生成,先选对库和运行环境
2.1 二维码生成技术方案对比:本地库、Web API、原生组件
VSTO 插件运行在 Office 进程内,本质是一个 .NET 程序集,所以生成二维码的思路和普通 .NET 应用完全一致。区别在于三件事:运行环境是 Office 主程序承载,界面是 Office 的 Ribbon 或任务窗格,输出目标是 Sheet 单元格、Word 文档段落或邮件正文。
二维码生成的常见路线有三条。第一条是本地生成,用 QRCoder、ZXing.Net、ThoughtWorks.QRCode 这类 .NET 库,在内存中绘制成 Bitmap,再插入文档。第二条是调用在线 API,把数据 POST 到服务端拿回图片,这种方式不推荐用在 VSTO 里,因为 Office 插件需要离线可用、数据要留在本地。第三条是调用系统自带的条码控件或第三方 COM 组件,成熟但笨重,早年被大量采用,现在基本被本地纯托管库取代。
| 方案 | 离线可用 | 依赖体积 | 样式定制能力 | 部署复杂度 | 推荐度 |
|---|---|---|---|---|---|
| QRCoder(本地) | 是 | 仅一个 DLL | 高(颜色、图标、边框) | 低 | 最高 |
| ZXing.Net(本地) | 是 | 两个 DLL | 中(侧重解码与标准条码) | 低 | 较高 |
| Web API 在线生成 | 否 | 无 | 取决于服务端 | 中(需网络与接口成本) | 低 |
| Office 自带条码控件 | 是 | 无 | 低(仅传统条码,二维码支持弱) | 中 | 不推荐 |
我一般会在新项目里选 QRCoder,它的 API 面向“按模块绘制”,能直接控制二维码每个点的尺寸、颜色、边框、中心 Logo,且不依赖 System.Drawing 以外的原生库。VSTO 场景下,最终产出物是 Bitmap 或 PNG 流,QRCoder 的 PngByteQRCode 和 QRCode 两个类正好覆盖这条链路。
2.2 VSTO 项目结构与 VB.NET 最小骨架
创建一个 VSTO 插件项目,常见做法是直接用 Visual Studio 的 “Office/SharePoint 开发” 模板,选 “Excel VSTO 工作簿” 或 “Word VSTO 文档” 之外的 “VSTO 外接程序”(Add-in)。区别在于:Add-in 是独立于文档的插件,任何 Excel 文件打开都会加载;工作簿插件则把代码绑定在特定文件上。对二维码工具来说,Add-in 更通用,分发后一次安装,所有文档可使用。
以 Excel Add-in 为例,项目里核心文件是 ThisAddIn.vb。默认代码只有启动和关闭事件,二维码功能需要通过自定义 Ribbon(XML 或设计器)暴露出来。如果临时验证流程,可以用 Sheet 的按钮(表单控件或 ActiveX 控件)触发代码,但正式交付我会用 Ribbon XML,因为 Ribbon 控件更稳定,不涉及 ActiveX 的信任与崩溃问题。
Imports System.Drawing Imports QRCoder Public Class QrCodeHelper ''' <summary> ''' 用 QRCoder 生成二维码位图 ''' </summary> Public Shared Function GenerateQrBitmap(content As String, size As Integer) As Bitmap Using generator As New QRCodeGenerator() Dim data As QRCodeData = generator.CreateQrCode(content, QRCodeGenerator.ECCLevel.Q) Using code As New QRCode(data) Return code.GetGraphic(size) End Using End Using End Function End Class这段代码做三件事:构造 QRCodeGenerator 对象,调用 CreateQrCode 把字符串编码为二维码数据,再用 QRCode 的 GetGraphic 输出为 Bitmap。重点在 CreateQrCode 的第二个参数,ECCLevel.Q 指的是 25% 容错率,适合文档被折叠、遮挡、小尺寸打印的办公场景,稍后会展开讲。
VB.NET 相比 C# 做 VSTO,Ribbon 的 XML 回调事件和 Add-in 生命周期代码没什么区别,唯一要留意的是闭包与事件注册的写法。VB 的 Handles 子句在 VSTO 里容易踩坑,建议统一用 AddHandler 显式挂接,避免窗体或控件被垃圾回收后事件不触发。
2.3 NuGet 引用与目标框架,最容易踩的环境坑
VSTO 项目在 .NET Framework 4.6.x 或 4.7.2 下最常见。QRCoder 依赖 System.Drawing,而 System.Drawing 在 .NET Framework 里是原生支持的程序集,配置最简单。如果项目升级到 .NET 6/8 的 VSTO(VSTO 官方不支持跨平台和 .NET Core,只能靠第三方扩展),System.Drawing.Common 在非 Windows 下会抛异常,要特别注意。
添加 NuGet 包的步骤固定:在项目上右键选 “管理 NuGet 程序包”,搜索 QRCoder,安装最新稳定版;同时确认 QRCoder 的依赖项(目前主要是 System.Drawing 相关包)没有被误删。安装完后检查项目的 app.config 里 bindingRedirect 是否正确生成,如果跳过了这一步,运行时可能报 “未能加载文件或程序集 QRCoder”。
提示:VSTO 插件默认以 x86 或 AnyCPU 编译,但 Office 64 位安装下必须把目标平台改为 x64。QRCoder 本身是纯托管代码不受影响,但 VSTO 运行时与 Office 主程序的位数必须一致,否则插件无法加载。
3. 最小可运行 Demo:把二维码写进 Excel 单元格区域
3.1 从代码到 Sheet:位图插入与定位逻辑
二维码生成完 Bitmap 只做了一半,另一半是把这个位图放到工作表的指定位置。常见的开放场景是:用户选中一个单元格,或输入一段 URL,点 Ribbon 按钮,二维码自动插入到所选单元格右侧。
Imports Microsoft.Office.Interop.Excel Imports System.Runtime.InteropServices Public Sub InsertQrCodeToSheet(sheet As Worksheet, targetRange As Range, content As String) ' 生成二维码图片 Dim qrBitmap As Bitmap = QrCodeHelper.GenerateQrBitmap(content, 20) ' 图片放到剪贴板,再用 Paste 方式插入,保证坐标可控 Clipboard.SetImage(qrBitmap) Dim targetCell As Range = targetRange.Offset(0, 1) sheet.Paste(targetCell) ' 释放位图资源 qrBitmap.Dispose() ' 按单元格宽度调整图片显示大小 Dim pic As Picture = sheet.Pictures(sheet.Pictures.Count) pic.Left = targetCell.Left pic.Top = targetCell.Top pic.Width = targetCell.Width * 3 pic.Height = targetCell.Height * 3 Marshal.ReleaseComObject(pic) End Sub这段代码的逻辑是用 Clipboard 作为中间通道,因为 Excel 的 Shapes.AddPicture 需要文件路径,而我们要的是内存位图。Clipboard.SetImage 会把 Bitmap 放入系统剪贴板,sheet.Paste 直接把剪贴板里的图片粘到目标单元格,再通过 Pictures 集合拿到刚粘贴的图片对象设置位置与大小。
参数的直观含义:targetRange 是你想作为“锚点”的单元格,二维码放在它的右侧一格;Width 和 Height 按单元格宽高的 3 倍放大,适合二维码需要扫出内容而不只是“贴在文档里”的展示场景。如果二维码需要严格匹配单元格大小(比如打印在固定表单里),把系数改为直接赋值即可。
3.2 不弹错、不闪烁的写入姿势与 COM 资源释放
VSTO 里操作 Excel 对象模型,最容易被人忽略的是 COM 资源释放。Excel 的互操作对象不是 .NET 托管对象,ReleaseComObject 或 Marshal.FinalReleaseComObject 应该用起来,否则 Excel 退出时会残留进程。更安全的方式是把 Application 的 DisplayAlerts 设为 False,插入图片时避免弹出确认框;操作结束后恢复原值。
Dim app As Excel.Application = TryCast(Globals.ThisAddIn.Application, Excel.Application) app.ScreenUpdating = False Try Dim ws As Excel.Worksheet = app.ActiveSheet Dim cell As Excel.Range = app.Selection InsertQrCodeToSheet(ws, cell, content) Finally app.ScreenUpdating = True End TryScreenUpdating 设为 False 会让写入过程不闪屏,尤其在批量插入多张二维码时,这个开关能把执行时间缩短一半以上。ActiveSheet 和 Selection 分别从 Application 取当前上下文,比遍历 Worksheets 定位更直接。注意 Finally 里恢复 ScreenUpdating,否则关闭插件后 Excel 界面可能一直处于不刷新状态。
4. 二维码参数的实用调优:容错、尺寸、中文和 Logo
4.1 ECC 容错级别选多少:不是越高越好
QRCoder 的 CreateQrCode 提供四个级别:L(7% 容错)、M(15%)、Q(25%)、H(30%)。容错越高,二维码能承受的遮挡和破损越多,但代价是信息密度增加,图上黑白模块更密,同样尺寸下识别距离变短。
在 Office 打印场景,推荐 Q 级。理由很具体:打印机的墨量不均、纸张褶皱、装订压边都会有遮挡,Q 级能容纳 25% 的码字损失;而 H 级在新增内容超过一定阈值时,会触发二维码升级版本(Version),模块剧增,导致图片拉伸后模糊。L 级适合 LED 屏或高质量印刷,不适合办公文档。
| ECC 级别 | 容错率 | 信息密度 | 推荐场景 |
|---|---|---|---|
| L | 7% | 低 | 高清屏幕、金属雕刻 |
| M | 15% | 中 | 一般印刷品 |
| Q | 25% | 较高 | Office 文档、打印、易遮挡场景 |
| H | 30% | 高 | 极小尺寸零件、磨损环境 |
4.2 尺寸与像素:GetGraphic 的参数到底控制什么
GetGraphic 有两个重载,一个只传 pixelsPerModule,另一个传前景色、背景色和 logo。pixelsPerModule 指每个黑白模块的边长像素数,不是整张图片的总像素。二维码由固定数量的小方块组成,内容越少模块数越少,内容越多模块数越多。想让二维码显示得大,不是直接调图片宽高,而是增大每模块像素数。
Dim qrCode = New QRCode(data) ' pixelsPerModule = 20,表示每个模块 20x20 像素 Dim bmp As Bitmap = qrCode.GetGraphic(20) ' 如果需要尺寸可控,先算出总宽再均分 Dim totalModules As Integer = qrCode.GetTotalModules() Dim targetWidth As Integer = 240 Dim ppModule As Integer = CInt(Math.Floor(targetWidth / totalModules)) qrCode.GetGraphic(ppModule)GetTotalModules 返回二维码版本对应的模块总数,比如 Version 2 是 25x25。当 targetWidth 固定时,按模块数算出单模块像素,生成的二维码打印出来边长远小于直接按 240 像素生成的图片,因为后者是纯放大、模块边缘发虚。想让打印扫码稳定,正确做法是先固定模块像素数,再让整体尺寸自适应。
4.3 中文与特殊字符:二维码内容要不要 URL 编码
二维码编码的内容本身是一串字节。中文文本直接传给 CreateQrCode,QRCoder 内部会用默认的编码规则处理,但遇到回车、换行、特殊符号时容易出问题。正式业务里,我建议对文本内容做标准化处理:
Public Shared Function NormalizeQrContent(raw As String) As String ' 去除首尾空白、统一换行符 raw = raw.Trim() raw = raw.Replace(vbCrLf, vbLf).Replace(vbCr, vbLf) ' 如果是 URL,做完整检测,不带协议时补全 If raw.Contains("://") = False And raw.StartsWith("www.") Then raw = "http://" & raw End If Return raw End Function二维码扫出来是纯文本,URL 编码取决于扫码终端。微信、支付宝扫码会识别 http 前缀自动跳转,但有些扫码 App 对直接输入域名不识别,补协议头是兼容性最稳妥的做法。二维码里不要存中文的参数值(比如链接里的 name=张三),要先用 System.Uri.EscapeDataString 编码,否则部分扫码器解析失败。
4.4 加 Logo 与前景色的边界条件
QRCoder 支持在二维码中央嵌入 Logo 图片,但 Office 文档场景下要控制 Logo 面积。Logo 的尺寸由 payload 的内部按比例提供,不能超过二维码整体面积的 30%,否则容错再高也会扫不出来。如果必须加 Logo,选 Q 级容错,并且把 Logo 区域限制在中心 20% 以内。
Dim bmp As Bitmap = qrCode.GetGraphic(20, Color.Black, Color.White, logoBitmap, iconBorderWidth:=2)iconBorderWidth 参数控制 Logo 周围的白边宽度,它能增大 Logo 和二维码模块之间的对比,避免深色 Logo 与黑色模块糊在一起。同时建议将前景色固定为 Color.Black,不要在彩色二维码上做“反白”或“低对比度”处理,Office 打印默认是黑白打印机,彩色依赖会让二维码实物识别率下降。
5. 部署的四道坎:加载失败、信任中心、依赖 DLL 与 64 位 Office
5.1 VSTO 插件的加载链路:从安装到 Excel 启动
VSTO 插件写好后,调试阶段会在 Visual Studio 里直接按 F5 启动 Excel,部署阶段则靠 ClickOnce 发布。VSTO 的加载原理是通过 Windows 注册表把插件程序集路径登记到 Office 应用的外接程序映射下。Excel 启动时读取注册表,再用 .NET 运行时加载程序集,任何一环出错都会导致插件不显示。
先确认项目属性里“发布”选项卡的安装模式。开发机测试选“从 CD-ROM 或 DVD-ROM 安装”,发给同事用“从 UNC 路径或网站安装”。VSTO 对安装路径有安全限制,UNC 路径要加到 Office 受信任位置,否则加载直接被拦。
5.2 加载失败时优先排查的顺序
加载失败的原因集中在四个方面:目标平台位数不匹配、.NET Framework 版本不对、QRCoder 相关依赖未随发布复制、Office 信任中心拦截。按频次从高到低排查:
检查 x64/x86:项目平台与 Office 位数必须一致,混搭时报错最隐蔽,因为开发机调试可能正常,部署到客户 64 位 Office 后消失。
检查 VSTO 运行时:Office 2010 后自带 VSTO 运行时,但精简版或 Office 365 企业版可能被裁剪,需要重新安装 vstor 运行库。
检查 ClickOnce 清单签名:无法加载时,Office 会显示“自定义项未加载”并写进事件日志,用事件查看器看 Application 下的 Error 来源 VSTO,能拿到具体堆栈。
检查 QRCoder.dll 是否存在:发布目录里要确认 QRCoder.dll 和依赖的 DLL 被作为“内容文件”发布,而不是放在开发者本机的 GAC 里。项目属性里把 QRCoder 的“本地复制”设为 True。
# 管理员权限下查看 VSTO 加载错误日志 Get-WinEvent -LogName Application | Where-Object { $_.ProviderName -match "VSTO" } | Select-Object -First 10 TimeCreated, Message这篇文章放一段命令是方便你在交付现场快速定位问题。VSTO 插件被加载时,Office 的 COM 加载项列表(Excel 选项 - 加载项 - COM 加载项)也会显示对应项,但“加载行为”列不会给出具体错误,只能判断加载是否成功。
5.3 证书信任与 SmartScreen 的沟通成本
VSTO 部署真正让人头疼的不是代码,是证书。开发机调试时 Visual Studio 会生成一个临时测试证书,插件在本机正常运行;发布到其他人的电脑时,Office 检查到 ClickOnce 清单签名不受信任,直接阻止加载。
常见做法是用公司内部的代码签名证书给 vsto 清单签名,然后在内网分发时把根证书装到客户机“受信任的根证书颁发机构”。没有证书的临时方案是让客户在安装后手动信任清单,但这在正式交付中流程上不可取。还有一种折衷做法是生成自签名证书,双击安装到受信任发布者,测试没问题再决定是否采购正规证书。
6. 批量生成与验证:最后把二维码做成一个可靠的功能
批量生成是 VSTO 二维码功能从“能跑”到“好用”的分水岭。常见场景是销售报表里前 50 行数据,每行需要生成一个带 ID 的二维码。此时要避免逐行 Clipboard 粘贴,因为剪贴板反复占用会导致 Excel 响应变慢甚至假死。更稳妥的方式是先生成所有 Bitmap,统一写入,最后一起释放。
Public Sub BatchInsertQrCodes(ws As Excel.Worksheet, rng As Excel.Range, titleColumn As Integer) Dim rowCount As Integer = rng.Rows.Count Dim images As New List(Of Tuple(Of Bitmap, Excel.Range))() For i As Integer = 1 To rowCount Dim cell = rng.Cells(i, 1) Dim content As String = cell.Value2.ToString() If String.IsNullOrEmpty(content) Then Continue For Dim targetCell = cell.Offset(0, titleColumn) Dim bmp As Bitmap = QrCodeHelper.GenerateQrBitmap(content, 20) images.Add(New Tuple(Of Bitmap, Excel.Range)(bmp, targetCell)) Next For Each item In images Clipboard.SetImage(item.Item1) ws.Paste(item.Item2) Next For Each item In images item.Item1.Dispose() Next End Sub这段代码里有两个关键设定。rng.Cells(i, 1) 只取选定区域的第一列作为二维码内容源,titleColumn 是相对偏移量,决定二维码插入在第几列。内存中先收集 Bitmap 和 Range 的配对关系,再一次执行粘贴,避免因为粘贴操作修改活动单元格位置导致定位漂移。
批量操作后应该做一次反向验证,拿一个解码库读出刚插入二维码的内容,和原数据做对比。QRCoder 只管生成,ZXing.Net 可以解码,引用 ZXing.Net 后这样验证:
Imports ZXing Public Shared Function DecodeQr(bmp As Bitmap) As String Dim reader As New BarcodeReader() Dim result = reader.Decode(bmp) If result IsNot Nothing Then Return result.Text End If Return Nothing End Function把每张刚从工作表里截取出来的图片做解码,如果内容不一致,多半是图片被 Excel 压缩或色彩模式变化导致,优先检查图片的插值模式。VSTO 插入的图片默认会做压缩,可以在插入后把图片对象的 Compression 属性改为无压缩,确保二维码不因图片处理而失真。至此,从生成、插入、部署到验证的完整闭环才真正建立起来。
本文还有配套的精品资源,点击获取