ZXing.NET 是 C# 中最稳定、兼容性好、文档全的二维码生成方案,支持 .NET Framework 4.5+ 及 .NET Core 3.1+ 以上全平台,推荐通过 NuGet 安装并配置 BarcodeWriter 与 QrCodeEncodingOptions(含 UTF-8、高容错、合理尺寸与边距),注意空输入校验、资源释放、UI 线程安全及文件保存健壮性。

ZXing.NET 是目前 C# 中最稳定、兼容性最好、文档最全的二维码生成方案,推荐直接用它,别折腾老库(如 ThoughtWorks.QRCode)或已停更的封装。它支持 .NET Framework 4.5+、.NET Core 3.1+ 和 .NET 5/6/7/8,WinForms、WPF、ASP.NET Core 全能跑。
安装 ZXing.NET 并初始化写入器
不手动下载 DLL,也不拖引用——用 NuGet 命令一行搞定:
dotnet add package ZXing.Net
或在 Visual Studio 包管理器控制台运行:
Install-Package ZXing.Net
然后在代码中引入命名空间:
using ZXing; using ZXing.Common; using ZXing.QrCode;
关键点:
-
BarcodeWriter是生成入口,必须指定Format = BarcodeFormat.QR_CODE - 必须显式配置
QrCodeEncodingOptions,否则中文乱码、尺寸失控、容错低 - 别漏掉
options.CharacterSet = "UTF-8",否则 TextBox 输入中文会变问号或报错
生成带中文内容的二维码(含防空校验)
常见错误:用户没输内容就点“生成”,writer.Write("") 直接抛 ArgumentNullException。
正确做法是加一层判断,并设置默认占位符:
private Bitmap GenerateQrCode(string content)
{
if (string.IsNullOrWhiteSpace(content))
content = "QR_EMPTY";
var writer = new BarcodeWriter
{
Format = BarcodeFormat.QR_CODE,
Options = new QrCodeEncodingOptions
{
DisableECI = true,
CharacterSet = "UTF-8",
Width = 300,
Height = 300,
Margin = 2,
ErrorCorrection = ErrorCorrectionLevel.H // 高容错,推荐 M 或 H
}
};
return writer.Write(content);
}
说明:
-
ErrorCorrectionLevel.H比默认的M更抗划伤/污损,适合打印场景 -
Margin = 2表示白边宽度(单位:模块数),太小(如 0)会导致扫码器无法识别 - 尺寸
Width/Height建议设为 200~400,再小易糊,再大无意义(手机摄像头解析力有限)
保存为 PNG 文件并防止文件冲突
直接 bitmap.Save("xxx.png") 很危险:并发请求、重复点击、路径不存在都会崩。
安全写法要包含三件事:目录创建 + 唯一文件名 + 异常后释放资源:
public string SaveQrCode(Bitmap bitmap, string folderPath, string fileName = null)
{
var dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, folderPath);
Directory.CreateDirectory(dir);
fileName ??= $"qrcode_{DateTime.Now:yyyyMMddHHmmssfff}.png";
var fullPath = Path.Combine(dir, fileName);
try
{
bitmap.Save(fullPath, ImageFormat.Png);
return fullPath;
}
catch (Exception ex)
{
throw new InvalidOperationException($"保存二维码失败: {ex.Message}", ex);
}
finally
{
bitmap?.Dispose(); // 必须释放,否则内存泄漏
}
}
注意:
- 不要用
AppDomain.CurrentDomain.BaseDirectory在 ASP.NET Core 中——改用IWebHostEnvironment.WebRootPath - 文件名里加毫秒级时间戳(
fff)可基本避免重名,无需 GUID - WinForms 中若直接赋给
PictureBox.Image,记得先Dispose()旧图,否则 GDI 句柄耗尽
WinForms 点击按钮生成并显示到 PictureBox
这是最典型场景,但新手常卡在两处:图片不显示、界面卡死(因没走 UI 线程)。
完整事件处理逻辑如下:
private void btnGenerate_Click(object sender, EventArgs e)
{
var text = textBoxInput.Text.Trim();
if (string.IsNullOrEmpty(text))
{
MessageBox.Show("请输入内容", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
try
{
var qrBitmap = GenerateQrCode(text);
// WinForms 必须在 UI 线程设置 Image
pictureBox1.Invoke((MethodInvoker)delegate
{
pictureBox1.Image?.Dispose();
pictureBox1.Image = qrBitmap;
});
}
catch (Exception ex)
{
MessageBox.Show($"生成失败:{ex.Message}");
}
}
关键提醒:
- 永远不要在非 UI 线程直接操作控件(比如后台线程调
pictureBox1.Image = ...) -
pictureBox1.Image赋值前不Dispose()旧图,多次点击后内存暴涨、程序变慢 - 如果后续要打印,别用
PictureBox.Image原图——它可能被缩放失真,应另存高清源图再送打印
生成二维码本身不难,难的是让每一处边界条件都稳住:空输入、中文、高并发、UI 线程、资源释放、跨平台路径。这些细节堆起来,才是生产可用的代码。
