☰
Microsoft BarCode Control 9.0:C#桌面程序条码生成与打印实战指南
2026/10/10 6:31:48 网站建设 项目流程

简介:微软条形码控件9.0的演示工程,面向.NET开发人员,用于在Windows应用程序中快速集成条形码生成、显示及打印能力。压缩包内共30个文件,主要包括C#源代码、可执行文件、动态链接库以及解决方案与工程文件,还附带资源文件和调试符号,整体大小仅57KB,是一份便于学习与改造的轻量级Demo。示例项目完整演示了该控件的实际用法:将控件拖放到窗体后,可通过属性设置条码类型、尺寸、颜色及数据;支持数据绑定,自动根据数据库或变量更新条码;开发者还能监听Paint等事件进行自定义绘制,或直接调用打印功能输出到打印机。代码中展示了Generate方法、Text属性、前景色与背景色设置等编程接口,并内置EAN-13等条码的校验位计算,保证数据准确,同时控件兼容较新的.NET Framework版本,可平滑融入现代Windows应用。当前已有6427人学习/下载,对于需要处理库存、物流或商品标码的工程师而言,通过阅读这份示例项目,可以快速掌握控件集成思路与关键代码,减少重复排查时间,显著提升条形码功能的开发效率。

1. Microsoft BarCode Control 9.0 是什么:桌面程序里最省事的条码生成方案

如果你接过“给产品打条码”这种需求,应该能理解那种尴尬:服务器不在手边、标签打印机还没接、开发周期又紧,却要在一周内把一套带条码的桌面管理端交出去。Microsoft BarCode Control 9.0 就是专门填这个坑的 ActiveX 条码控件,它把条码生成这件事从“调第三方库、处理图片流、自绘编码图形”压成“拖个控件、设两个属性、直接打印”。它能解决的是桌面环境下的条码生成和打印,不占服务器资源,也不需要你懂 Code 128 的编码表。适合做进销存、固定资产、样品标签这类内部管理软件,也适合那些每年只打几千张标签、为一个小功能去采购昂贵条码机驱动的团队。这篇就沿着“装控件 → 生成条码 → 打印输出 → 排坑”这条线,把完整落地过程讲清楚。

2. 把控件装进你的工程:注册、引用与第一个可运行窗体

2.1 先确认系统里有没有这个控件

这个控件早期会随一些微软开发工具和桌面组件自动注册,很多老机器上其实已经存在,只是你从未注意过。最直接的确认方式是按 Win + R 运行regedit,在注册表编辑器里直接搜索关键词BarCode,如果存在类似MSBarCode的 ProgID 节点,说明控件已经在系统里了。不想动注册表的话,也可以直接在开发机器的命令行里查:

Get-ChildItem C:\Windows\SysWOW64 -Filter "*.ocx" | Where-Object { $_.Name -match "barcode|msbc" }

这段命令的作用是在 64 位系统的 32 位 OCX 目录里查找跟条码相关的 ActiveX 控件文件。因为多数旧版 ActiveX 条码控件都是 32 位组件,默认注册路径就在SysWOW64下。如果查不到任何文件,说明这台机器上确实没装过,需要先找安装包;如果能查到,说明系统层已经有控件了,接下来只需要确认它在“组件服务”列表里可见。

注意:文件存在不等于注册表信息完整,OCX 控件必须经过regsvr32注册才能在开发工具里被找到了引用。所以上面两步都做了之后,更可靠的判断是在下一步的“添加 COM 引用”对话框里直接搜索,能看到条目才算真正可用。

2.2 命令行注册:regsvr32 与 32/64 位差异

如果确认系统里有 OCX 文件但开发环境里找不到,最常见的原因就是没注册或注册表信息缺失。注册这种 ActiveX 控件的方式是调用regsvr32,这是 Windows 自带的组件注册工具。常见做法是以管理员身份打开命令行,然后执行下面两条中的任意一条:

regsvr32 C:\Windows\SysWOW64\MSBCODE9.OCX
regsvr32 C:\Windows\System32\MSBCODE9.OCX

第一条是针对 64 位系统上的 32 位 OCX,第二条是针对原生 32 位系统。如果你把文件放到了其他目录,自己改路径就行。注册成功的标志是弹出一个“DllRegisterServer 成功”的提示框。

这里要说一个高频踩坑点:如果你在 64 位 Windows 上手动把 OCX 拷到了System32目录再执行regsvr32,它可能表面显示注册成功,但 32 位工程里照样找不到。因为 64 位系统的System32里跑的是 64 位注册表视图,跟 32 位 COM 引用不是同一套。反过来,如果你开的是 64 位开发工具去引用一个 32 位 OCX,工具会直接无视它。这不是控件坏了,是系统把 32 位和 64 位的注册表视图隔离开了。遇到这种情况,先确认文件物理位置和工具位数是否匹配,再决定注册哪条路径。

注册完成后,可以再次打开“添加引用”对话框,按名称搜索BarCode,能看到“Microsoft BarCode Control 9.0”这样的条目就说明注册成功。如果还是看不到,先检查是不是用错了 regsvr32 的路径,再检查杀毒软件有没有拦截 OCX 的写入动作,我见过不少机器是被安全软件静默拦截了注册表写入。

2.3 在 C# 工程里添加 COM 引用并拖入窗体

这个控件最常见的应用场景是老的桌面管理系统,很多还在用 C# WinForms 维护,所以下面用 C# 工程来说明。新建一个 WinForms 项目之后,在解决方案资源管理器里右键点击“引用”,选择“添加引用”,在左侧切换到“COM”选项卡,在搜索框里输入BarCode,勾选出现的控件条目并确定。这一步会生成互操作程序集,C# 代码里才能把控件当成普通控件来使用。

添加完成后,工具箱里通常会出现一个新的条码控件图标。直接把它拖到Form上,控件会以一个小方框的形式呈现在窗体设计器里。这时候属性窗口会列出它的成员列表——注意,ActiveX 控件暴露的属性名在不同互操作包装类里可能有一点前缀差异,比如某些环境里属性名是Style,某些新生成的环境里是get_Style之类。建议先选中控件,在属性窗口里翻一遍它的成员,心里有个底。

// 拖入控件后,先试着在窗体 Load 事件里设置最简单的两个属性 private void Form1_Load(object sender, EventArgs e) { // Value 是条码承载的内容:一串编号或文本 barCodeControl1.Value = "TEST-0001"; // ShowText 控制条码下方是否显示明文 barCodeControl1.ShowText = true; }

这段代码逻辑很简单:Value是待编码的内容,ShowText是是否在条码下方打印一行人眼可读的字符。为什么先跑通这两项?因为只要这两项正常,就说明控件注册正常、互操作引用正常、渲染管线正常,排除了九成环境问题。很多项目卡在第一步,其实不是代码问题,而是引用没添加成功。

2.4 第一个可运行窗体:输入字符立刻出条码

为了确认整个链路畅通,我建议先做一个最简验证窗体:一个文本框、一个条码控件、一个按钮。文本框输入内容,按钮点击后把内容赋给控件的Value属性,条码立即刷新。这一步不做打印、不做图片导出,只看控件能不能在界面上正确渲染。

private void button1_Click(object sender, EventArgs e) { if (string.IsNullOrWhiteSpace(textBox1.Text)) { // 空内容会导致部分码制报编码异常,先做防空判断 return; } try { barCodeControl1.Value = textBox1.Text; barCodeControl1.Refresh(); } catch (Exception ex) { // 常见异常:当前码制不支持该字符集 MessageBox.Show(ex.Message); } }

这段代码里做了两个容易被忽略的动作:一是空值拦截,因为部分码制对空字符串会直接抛异常;二是Refresh()强制重绘,避免界面上的旧内容残留。实际开发中我发现Value赋值之后控件通常会自动重绘,但把它写成显式调用能让行为更可预期。如果文本框里输入的是纯英文和数字,默认码制基本都能渲染出来;如果输入了中文、特殊符号,就会触发异常弹窗,这会很自然地把我们引向下一章要讲的码制选择问题。

3. 生成条码:Style、Value 与导出图片的完整姿势

3.1 最小代码:把一串编号变成 Code 128 条码

条码生成不是“画几条黑线”那么简单,不同码制对字符集、长度、校验位的要求都不一样。这个控件把底层编码封装起来了,但你必须知道该选哪种码制,以及它支持什么字符。最常用的通用码制是 Code 128,它密度高、支持全 ASCII、能编码数字和字母混合的字符串,适合做内部管理编号。下面是生成 Code 128 的最小编程动作:

// 先切换到 Code 128 码制 barCodeControl1.Style = STYLE_CODE128; // 再把数据交给控件 barCodeControl1.Value = "PO-2025-0001"; // 最后控制渲染效果 barCodeControl1.ShowText = true; barCodeControl1.Refresh();

这里Style就是码制选择,STYLE_CODE128是控件互操作层提供的一个枚举常量。为什么建议先用 Code 128?因为很多老项目里的数据是字母数字混合的流水号,Code 39 也能做,但同样是混合内容 Code 128 的宽度要短一截,打印小标签时更不容易超出纸宽。如果你的编号主要由纯数字组成且长度固定,EAN-13 更贴合零售扫描环境,但它对长度有强制要求,不是随随便便就能用的。这段代码里真正的“技术含量”是码制和数据内容的匹配:把 20 位超长字符串塞给 EAN-13,控件就会直接报警或截断,这不是控件 bug,而是码制本身的规则限制。

3.2 Style 枚举:怎么选码制,Code 39 / Code 128 / EAN-13

每个条码控件的 Style 枚举数值都不一样,别指望换一个环境数字还能通用。我在实际项目里的习惯是,在代码里优先使用枚举成员名而不是写死数字,这样换版本时至少编译器能帮你发现错误。下表是几种常见码制的选型对照,数值列故意不填,因为不同版本真的不一样,用了反而误导你:

码制典型用途支持字符宽度效率常见坑
Code 39内部资产、库存标签大写字母 + 数字 + 少量符号较宽长度一长就超出标签纸
Code 128通用流水号、物流追踪全 ASCII紧凑校验方式多样,别手动预编
EAN-13零售商品条码纯数字 13 位固定长度必须合规,不能随意截断
QR Code扫码跳转、日期批次并存中英文、URL面状打印尺寸过小会扫不出来

怎么查你自己环境里的枚举值?选中控件后打开对象浏览器,在左侧搜索Style,点击关联的枚举类型,就能看到每个成员对应的数字。我一般会把常用码制写成一个配置项存在数据库或配置文件里,运行时再映射到控件的 Style 枚举,这样以后换码制不用改代码逻辑。要注意的是:并不是每个版本的控件都内置 QR 码,如果你需要二维码但当前版本不支持,就得考虑兼容方案,比如把内容转成短连接再生成 Code 128,或者换控件版本。这一点要在选型阶段就确认,不然做到一半再换,整个打印模块都要推倒重来。

3.3 导出图片:直接存 PNG 时要注意的两个点

很多业务并不要求即时打印,而是希望先生成一批条码图片,存到共享目录里,再由其他程序或人工挑选打印。导出图片是这个控件使用频率很高的功能,做法也简单:把控件渲染到内存里的 Bitmap,再保存为 PNG。

using (Bitmap bmp = new Bitmap(barCodeControl1.Width, barCodeControl1.Height)) { // DrawToBitmap 是 WinForms 控件通用的渲染方法,ActiveX 控件也适用 barCodeControl1.DrawToBitmap(bmp, new Rectangle(0, 0, barCodeControl1.Width, barCodeControl1.Height)); // 保存为 PNG,白底黑条,适合打印 bmp.Save("C:\\Labels\\barcode_0001.png", System.Drawing.Imaging.ImageFormat.Png); }

这段代码第一个要注意的点是new Bitmap的尺寸。如果控件的实际显示尺寸和条码真实物理尺寸不一致,导出图片的宽高比就会失真。我建议在导出前先把控件的宽高设置成目标标签的实际像素尺寸,比如按 300 DPI 计算:标签宽 6 厘米,换算成像素大约是 709 像素。第二个点是背景色,控件默认背景可能是灰色或渐变,直接保存到图片里打印时不仅费墨,还可能影响扫码枪识别,下一章的避坑部分会专门讲怎么把背景锁成纯白。

3.4 让条码更漂亮:Caption、ShowText 与背景色

实际做标签时,条码下方通常要显示一行说明文字,比如“订单号:PO-2025-0001”或“生产日期:2025-03-12”。这个控件里ShowText管的是“条码本身对应的明文内容”,Caption管的是“条码上方的额外标题文字”,两者不要混用。

barCodeControl1.Value = "PO-2025-0001"; barCodeControl1.ShowText = true; // 条码正下方显示 PO-2025-0001 barCodeControl1.Caption = "采购订单"; // 条码上方显示中文说明

这段代码里Caption相当于是业务标签里的备注行,你可以在打印前把订单来源、责任人等额外信息填进去。但要注意中文标题在条码控件里的可读性依赖字体环境,有些精简版系统缺少中文字体,Caption 部分就会显示成方块。另外还要提醒一个细节:如果在打印前设置了BackColor,要确认它到底作用在条码区域还是整个控件区域,这决定了你导出图片时是白色背景还是带浅色底纹。保守做法是生成前显式设置一次背景色,别依赖控件的默认值。

4. 把条码送到打印机:三条路线与必调参数

4.1 路线一:用 DrawToBitmap 把控件画进打印页

打印条码的本质是“把条码图形以正确的物理尺寸输出到纸面”。最容易理解的做法是先把控件渲染成 Bitmap,再通过PrintDocument把图片画到打印页面上。这个方案适合中小批量打印,因为代码直白,不依赖任何报表组件。

Bitmap bmp; private void btnPrint_Click(object sender, EventArgs e) { bmp = new Bitmap(barCodeControl1.Width, barCodeControl1.Height); barCodeControl1.DrawToBitmap(bmp, new Rectangle(0, 0, barCodeControl1.Width, barCodeControl1.Height)); PrintDocument pd = new PrintDocument(); pd.PrintPage += Pd_PrintPage; pd.Print(); } private void Pd_PrintPage(object sender, PrintPageEventArgs e) { // 计算实际打印尺寸:宽 6 厘米,换算成百分之一英寸 float targetWidth = 6.0f / 2.54f * 100f; float targetHeight = targetWidth * bmp.Height / bmp.Width; e.Graphics.DrawImage(bmp, 0, 0, targetWidth, targetHeight); e.HasMorePages = false; bmp.Dispose(); }

这段代码的关键在最后三行的尺寸换算。Graphics.DrawImage的重载里,后面的两个参数单位是“百分之一英寸”,所以 6 厘米要先除以 2.54 换算成英寸,再乘以 100 才能得到图形接口需要的数值。很多人直接传像素值,结果打出来的条码要么大得离谱、要么小到扫不出,这就是单位换算没做对。

4.2 路线二:导出图片,让打印任务自己排队

如果打印量不大但场景零散,或者打印机是共享的,不一定非要在程序里接管打印任务,把条码生成成 PNG 图片再交给标签软件或 Windows 自带的打印队列可能更省事。这个思路特别适合“程序负责生成、人负责打印”的变通场景,比如仓库临时补打一张标签。

string filePath = $"C:\\Labels\\{DateTime.Now:yyyyMMdd_HHmmss}.png"; barCodeControl1.DrawToBitmap(bmp, new Rectangle(0, 0, barCodeControl1.Width, barCodeControl1.Height)); bmp.Save(filePath, ImageFormat.Png); // 调用系统图片查看器打印 System.Diagnostics.Process.Start(filePath);

这段代码走的是“生成图片 + 系统打开”的路线。把图片打开之后,用户在图片查看器里按 Ctrl + P 打印,尺寸可以自己在打印预览里设置。这条路线省掉了PrintDocument的编码成本,代价是流程多了一步人工介入,不能全自动。适合一天打印几十张、且每张需要人工核对内容的场景。如果你做的是自动流水线,依然要走PrintDocument或下面的报表路线。

4.3 路线三:报表里嵌 OLE 对象

很多老管理系统都接了报表组件,像 Crystal Reports 一类的设计器支持插入 OLE 对象,而这个条码控件可以作为 OLE 对象嵌入报表模板。这样做的最大好处是:条码直接跟报表字段绑定,数据源变化时条码自动刷新,不需要写针对性打印代码。缺点是模板设计阶段比较繁琐,而且报表组件和 ActiveX 控件的兼容性偶尔会出现“预览正常、打印空白”的情况。

我一般不会在项目一开始就推报表嵌入路线,因为它的调试成本偏高。但如果你手里的项目已经有报表层了,把条码加进去是最自然的扩展。具体操作:在报表设计器里插入 OLE 对象,选择已注册的条码控件,然后通过报表字段映射把数据源里的编码字符串传给控件的Value。要注意的是:报表字段传给条码控件时,一定要在数据层提前做一次字符集校验,别让一个带换行符的备注字段直接冲击条码编码器,否则打印时才会发现条码区域全部空白。

4.4 打印必调参数:DPI、纸张、边距与条码尺寸

真正决定条码能不能被扫描枪读出来的,不是控件本身,而是打印输出的物理参数。下面这几个参数是我每次做标签打印必查的:

参数推荐范围检查方式失败表现
打印分辨率300 DPI 起打印机驱动属性低 DPI 下细线条糊成一片
条码宽度不小于 2-3 厘米直尺量标签太窄时扫码枪无法聚焦
静区宽度至少为窄条宽的 10 倍观察条码左右留白扫描器识别不稳定
纸张类型热敏纸选对应的驱动按纸张材质选碳带不匹配,打印模糊

关于 DPI 我要说一个很容易翻车的点:控件渲染用的是屏幕 DPI 逻辑,而打印机工作在不同 DPI 下,如果直接把控件的像素尺寸当成打印尺寸交付,绝大多数标签会显琐碎。正确做法是明确目标标签的物理尺寸,再按打印机的 DPI 反推像素。同样一张 6 厘米宽的标签,在 203 DPI 和 300 DPI 的打印机上,像素要求完全不一样。这一点必须在程序里写清楚,不能指望用户去手动调。

5. 避坑与排查:注册失败、扫码枪不识别、打印尺寸不对

5.1 现象:注册成功,工程里却找不到控件

我之前在帮一个仓库管理系统加条码功能时就遇到这个情况:管理员在命令行执行regsvr32后系统明确提示成功,但打开 Visual Studio 的“添加引用”对话框,搜不到任何 barcode 相关条目。排查后发现,执行注册的是 64 位版本的regsvr32,而工程编译目标是 Any CPU,实际以 64 位进程运行,COM 引用列表里的 32 位 OCX 跟不上来。解决方式是确认控件是一个 32 位 OCX,然后把工程编译目标固定为 x86,重新添加引用。这个问题从现象到原因都对得上,但如果你不了解 32/64 位 COM 注册表隔离机制,真的会蒙很久。

5.2 现象:条码打印出来扫码枪不识别

这是最让人崩溃的问题——条码肉眼看着非常清晰,黑是黑白是白,但扫码枪就是没有任何反应。我遇到过的典型案例是:打印时为了省纸,把条码区域压缩成 1.5 厘米宽,线条密度高到普通扫码枪无法分辨。另一个常见原因是静区不足,条码图案紧贴左右边缘,没有保留足够空白,扫描器的激光无法定位起始位置。解决方式是:先在控件里把条码区域之外的两侧留白,打印后实测确认静区宽度至少有窄条宽度的 10 倍。如果标签纸实在太小,就换更紧凑的码制,比如从 Code 39 换成 Code 128,而不是继续压缩现有码制的尺寸。

5.3 现象:打印尺寸偏大或偏小

有的条码打出来明显比设计尺寸大一圈,或者缩在标签角落里。这个十有八九是单位换算出了岔子。DrawImage的打印接口里如果不做“厘米转百分之一英寸”的换算,把所有数字直接当成像素传进去,打印预览就会随风奔跑。我的排查习惯是先打印一张带参考边框的测试图,上面画一个已知边长 5 厘米的矩形,再用直尺量打印结果。只要标准矩形尺寸准了,条码区域的物理尺寸就基本能对,剩下的就是微调条码在页面上的起点偏移。

5.4 现象:控件在 64 位程序里编译报错

很多开发者以为把“任何 CPU”改成“x64”会让程序跑得更快,结果一编译,引用层直接红一片。原因还是老话:这个 ActiveX 条码控件是 32 位组件,64 位进程无法加载它。解决方式不是找什么“64 位补丁版本”,而是把工程编译目标改成 x86,或者干脆使用长时间支持 32 位的进程架构。如果你是在一台 64 位系统上部署,x86 程序完全能跑,别为了“看着现代”去挑战系统兼容性。这是我看过最多人卡住的地方,也是最容易自我怀疑的地方——报错信息根本不会告诉你“因为控件是 32 位”,只会抛一个 COM 类无法创建。

5.5 现象:导出的条码图片背景不是纯白

条码图片导出后带灰色底纹或者渐变底,打印时不仅难看,部分扫描枪在低对比度下还会识别失败。原因是控件自身的背景色用了接近白色但不是纯白的默认值。解决方法是导出前显式设置控件背景为纯白:

barCodeControl1.BackColor = Color.White; barCodeControl1.Refresh();

这段代码解决的不只是视觉问题。扫码枪识别依赖条码区域与静区的对比度,背景一旦不是纯白,图形边缘的灰度值会漂移,识别率直线下降。我现在的习惯是:任何导出和打印动作之前,都把背景色、前景色显式设置一遍,靠“默认值”早晚在某个版本的控件上栽跟头。

6. 进阶:流水号批量打印与一条验证习惯

6.1 用数据源驱动循环生成条码

批量打印时不要一条条写死,让数据来源驱动循环,代码更简洁,生产上也更可靠。

foreach (string code in orderCodeList) { barCodeControl1.Value = code; barCodeControl1.Refresh(); using (Bitmap bmp = new Bitmap(barCodeControl1.Width, barCodeControl1.Height)) { barCodeControl1.DrawToBitmap(bmp, new Rectangle(0, 0, barCodeControl1.Width, barCodeControl1.Height)); string fileName = $"C:\\Labels\\{code}.png"; bmp.Save(fileName, ImageFormat.Png); } }

这个循环里有三个细节值得注意:一是Refresh()必须在赋值之后立即调用,确保控件渲染的就是新值;二是位图对象用了using,避免批量生成时内存句柄泄漏;三是保存文件名直接用业务编码,方便人工核对。如果数据源来自数据库,就换成DataReader或分页列表,别一次性把所有记录塞进内存。

6.2 打印前做一次扫描自检

我养成了一个小习惯:每次改完条码相关代码,先拿一张刚出来的打印件做扫描测试,而不是盯着屏幕看效果。屏幕上的条码再清楚,也不代表印刷后的墨量和边缘锐度达标。没有扫码枪时,我会用手机相机微距拍一张条码照片,放大检查线条边缘是否平滑、有没有墨点粘连。这一招能提前发现大部分打印质量问题。

6.3 我的最后一道工序

最后一件事:我会在批量打印任务里固定加入一张测试页,内容是一组包含数字、字母和特殊符号的验证条码,打印后先扫描,全部通过才开始正式单据。这个习惯帮我在几次现场部署里躲过了晚间突击打印翻车的尴尬。现在不管你用什么控件版本、纸张多紧张,我都建议你保留下“先打测试页再跑正式任务”的流程。任何控件都有它的小脾气,而验证一次的成本远比重新打印一批低得多。希望这个习惯能帮到你少踩一些坑。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询