C#实战:如何用TSCLIB.DLL高效驱动TSC打印机打印条码和二维码(附完整代码)
如果你正在接手一个需要与TSC条码打印机打交道的C#项目,尤其是从旧系统改造而来,那么你很可能已经体会过那种混合着兴奋与头疼的感觉。兴奋在于,硬件交互总能带来一种“让机器动起来”的成就感;头疼则往往源于那些看似简单、实则暗藏玄机的细节:DLL引用失败、打印位置对不上、标签内容错位,或者更糟——打印机毫无反应。市面上很多教程只给出一段孤立的代码片段,当你兴冲冲地复制粘贴进自己的项目后,却发现它像一把没有钥匙的锁,怎么也打不开通往稳定打印的大门。
这篇文章就是为你准备的。我们不打算重复那些基础的API声明,而是聚焦于如何构建一个健壮、可维护、且易于调试的C#打印模块。我会分享在实际项目中趟过的坑、总结出的最佳实践,以及一个可以直接集成到你生产环境中的PrinterService类。无论你是需要打印商品条码、物流面单,还是带有复杂布局和二维码的资产标签,这里提供的思路和代码都能帮你把想法快速、可靠地变成握在手中的实体标签。
1. 项目基石:构建稳健的TSCLIB.DLL集成层
直接裸调DLL函数是危险的,它让业务代码与底层硬件API紧密耦合,任何一点变动都可能引发连锁反应。我们的第一步,是为这些原始的DLL调用建立一个安全的、面向对象的“隔离层”。
1.1 封装核心API:错误处理与资源管理
一个健壮的封装,首要任务是管理好打印机的“生命周期”(打开、操作、关闭)和异常。我们创建一个TSCPrinterInterop类,它不包含任何业务逻辑,只负责与TSCLIB.dll的安全对话。
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace YourProject.Printing.Core
{
/// <summary>
/// TSCLIB.dll 原生函数的安全封装层。提供基本的错误检查和资源管理。
/// </summary>
public class TSCPrinterInterop : IDisposable
{
private bool _portOpened = false;
private readonly string _printerName;
// 核心DLL导入 - 这里只列关键部分,实际需根据DLL版本补充
[DllImport("TSCLIB.dll", EntryPoint = "openport", CharSet = CharSet.Ansi)]
private static extern int OpenPortInternal(string printername);
[DllImport("TSCLIB.dll", EntryPoint = "closeport")]
private static extern int ClosePortInternal();
[DllImport("TSCLIB.dll", EntryPoint = "setup", CharSet = CharSet.Ansi)]
private static extern int SetupInternal(string width, string height, string speed,
string density, string sensor, string vertical, string offset);
[DllImport("TSCLIB.dll", EntryPoint = "clearbuffer")]
private static extern int ClearBufferInternal();
[DllImport("TSCLIB.dll", EntryPoint = "printlabel", CharSet = CharSet.Ansi)]
private static extern int PrintLabelInternal(string set, string copy);
[DllImport("TSCLIB.dll", EntryPoint = "usbportqueryprinter")]
private static extern int UsbPortQueryPrinterInternal();
public TSCPrinterInterop(string printerName)
{
if (string.IsNullOrWhiteSpace(printerName))
throw new ArgumentException("打印机名称不能为空", nameof(printerName));
_printerName = printerName;
}
/// <summary>
/// 打开打印机端口,并执行基础设置。这是所有打印操作的前提。
/// </summary>
public void OpenAndSetup(PrinterSetupConfig config)
{
int result = OpenPortInternal(_printerName);
if (result != 1) // 通常1表示成功,但需查阅TSC手册确认
throw new PrinterException($"无法打开打印机端口 '{_printerName}'。错误码: {result}");
_portOpened = true;
// 应用设置
result = SetupInternal(
config.Width.ToString(),
config.Height.ToString(),
config.Speed.ToString(),
config.Density.ToString(),
config.Sensor.ToString(),
config.Vertical.ToString(),
config.Offset.ToString()
);
if (result != 1)
{
ClosePortInternal(); // 尝试清理
_portOpened = false;
throw new PrinterException($"打印机设置失败。错误码: {result}");
}
ClearBufferInternal(); // 开始新任务前清空缓冲区是好习惯
}
/// <summary>
/// 查询打印机状态。在关键操作前调用,可以避免许多问题。
/// </summary>
public PrinterStatus QueryStatus()
{
if (!_portOpened)
return PrinterStatus.PortClosed;
int statusCode = UsbPortQueryPrinterInternal();
return (PrinterStatus)statusCode;
}
/// <summary>
/// 执行打印并处理可能的异常。
/// </summary>
public void Print(int copies = 1)
{
var status = QueryStatus();
if (status != PrinterStatus.Ready)
throw new PrinterException($"打印机未就绪,当前状态: {status}");
int result = PrintLabelInternal("1", copies.ToString()); // "1"通常代表打印一份的集合
if (result != 1)
throw new PrinterException($"打印命令执行失败。错误码: {result}");
}
public void Dispose()
{
if (_portOpened)
{
ClosePortInternal();
748



被折叠的 条评论
为什么被折叠?



