C# USB條碼掃描器:構建完整的掃描應用程式
USB 條碼掃描器作為標準鍵盤輸入裝置連接到 C# 應用程序,將掃描的資料作為鍵入的字元發送,然後按 Enter 鍵。 這種 HID 鍵盤楔形行為使整合變得簡單——您的應用程式無需任何特殊驅動程式或 SDK 即可接收文字輸入。 IronBarcode處理原始輸入以驗證格式、提取結構化資料並產生回應條碼,將簡單的掃描事件轉換為庫存管理、零售銷售點和物流追蹤系統的完整資料管道。
零售、倉儲和製造營運都依賴準確、快速的條碼掃描。 當開發人員將 USB 掃描器連接到 Windows Forms 或 WPF 應用程式時,掃描器的行為與鍵盤完全相同——資料會顯示在文字方塊中,按下 Enter 鍵表示已接收到完整的條碼。 真正的挑戰不在於如何取得數據; 它正在正確處理。 IronBarcode 的條碼驗證功能可檢查格式完整性,提取批號或 GS1 應用識別碼等字段,並可立即產生新的條碼作為回應。
本指南將逐步引導您建立一個可用於生產環境的 C# USB 條碼掃描器應用程式。您將安裝庫、擷取掃描器輸入、驗證條碼格式、產生回應標籤,並組裝一個高吞吐量的基於佇列的處理器。 每個部分都包含完整的、可運行的程式碼,目標框架為 .NET 10,並在適當情況下採用頂級語句樣式。
如何使用 C# 實作 USB 條碼掃描器?
為什麼 HID 鍵盤楔形模式能夠簡化整合?
大多數 USB 條碼掃描器出廠時預設為 HID 鍵盤楔形模式。 當您將 USB 裝置插入 Windows 電腦時,作業系統會將其註冊為 USB 儲存裝置(用於設定)和鍵盤(用於資料輸入)。 當掃描條碼時,裝置會將解碼後的條碼值轉換為按鍵,並將其發送到目前具有焦點的應用程式窗口,並在末尾添加回車符。
從 C# 開發人員的角度來看,這意味著您不需要供應商 SDK、COM 庫或特殊的 USB API。只需一個帶有 KeyDown 事件處理程序的標準 TextBox 即可捕獲輸入。 主要的整合挑戰在於區分掃描器輸入和真正的鍵盤輸入。 掃描器通常會在很短的時間內(通常不到 50 毫秒)完成所有字元的掃描,而人類打字則會將擊鍵過程分散到數百毫秒內。 控制連擊時間是過濾掉意外按鍵的可靠方法。
專業級掃描器還支援串列(RS-232 或虛擬 COM 連接埠)和直接 USB HID 模式,讓您可以更好地控制前綴/後綴字元和掃描觸發器。 下面這種介面模式可以處理這兩種情況:
public interface IScannerInput
{
event EventHandler<string> BarcodeScanned;
void StartListening();
void StopListening();
}
public class KeyboardWedgeScanner : IScannerInput
{
public event EventHandler<string> BarcodeScanned;
private readonly TextBox _inputBox;
private readonly System.Windows.Forms.Timer _burstTimer;
private readonly System.Text.StringBuilder _buffer = new();
public KeyboardWedgeScanner(TextBox inputBox)
{
_inputBox = inputBox;
_burstTimer = new System.Windows.Forms.Timer { Interval = 80 };
_burstTimer.Tick += OnBurstTimeout;
_inputBox.KeyPress += OnKeyPress;
}
private void OnKeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == (char)Keys.Enter)
{
_burstTimer.Stop();
string value = _buffer.ToString().Trim();
_buffer.Clear();
if (value.Length > 0)
BarcodeScanned?.Invoke(this, value);
}
else
{
_buffer.Append(e.KeyChar);
_burstTimer.Stop();
_burstTimer.Start();
}
e.Handled = true;
}
private void OnBurstTimeout(object sender, EventArgs e)
{
_burstTimer.Stop();
_buffer.Clear(); // incomplete burst -- discard
}
public void StartListening() => _inputBox.Focus();
public void StopListening() => _inputBox.Enabled = false;
}
public class SerialPortScanner : IScannerInput
{
public event EventHandler<string> BarcodeScanned;
private readonly System.IO.Ports.SerialPort _port;
public SerialPortScanner(string portName, int baudRate = 9600)
{
_port = new System.IO.Ports.SerialPort(portName, baudRate);
_port.DataReceived += OnDataReceived;
}
private void OnDataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
string data = _port.ReadLine().Trim();
if (data.Length > 0)
BarcodeScanned?.Invoke(this, data);
}
public void StartListening() => _port.Open();
public void StopListening() => _port.Close();
}
public interface IScannerInput
{
event EventHandler<string> BarcodeScanned;
void StartListening();
void StopListening();
}
public class KeyboardWedgeScanner : IScannerInput
{
public event EventHandler<string> BarcodeScanned;
private readonly TextBox _inputBox;
private readonly System.Windows.Forms.Timer _burstTimer;
private readonly System.Text.StringBuilder _buffer = new();
public KeyboardWedgeScanner(TextBox inputBox)
{
_inputBox = inputBox;
_burstTimer = new System.Windows.Forms.Timer { Interval = 80 };
_burstTimer.Tick += OnBurstTimeout;
_inputBox.KeyPress += OnKeyPress;
}
private void OnKeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == (char)Keys.Enter)
{
_burstTimer.Stop();
string value = _buffer.ToString().Trim();
_buffer.Clear();
if (value.Length > 0)
BarcodeScanned?.Invoke(this, value);
}
else
{
_buffer.Append(e.KeyChar);
_burstTimer.Stop();
_burstTimer.Start();
}
e.Handled = true;
}
private void OnBurstTimeout(object sender, EventArgs e)
{
_burstTimer.Stop();
_buffer.Clear(); // incomplete burst -- discard
}
public void StartListening() => _inputBox.Focus();
public void StopListening() => _inputBox.Enabled = false;
}
public class SerialPortScanner : IScannerInput
{
public event EventHandler<string> BarcodeScanned;
private readonly System.IO.Ports.SerialPort _port;
public SerialPortScanner(string portName, int baudRate = 9600)
{
_port = new System.IO.Ports.SerialPort(portName, baudRate);
_port.DataReceived += OnDataReceived;
}
private void OnDataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
string data = _port.ReadLine().Trim();
if (data.Length > 0)
BarcodeScanned?.Invoke(this, data);
}
public void StartListening() => _port.Open();
public void StopListening() => _port.Close();
}
Imports System
Imports System.Text
Imports System.Windows.Forms
Imports System.IO.Ports
Public Interface IScannerInput
Event BarcodeScanned As EventHandler(Of String)
Sub StartListening()
Sub StopListening()
End Interface
Public Class KeyboardWedgeScanner
Implements IScannerInput
Public Event BarcodeScanned As EventHandler(Of String) Implements IScannerInput.BarcodeScanned
Private ReadOnly _inputBox As TextBox
Private ReadOnly _burstTimer As Timer
Private ReadOnly _buffer As New StringBuilder()
Public Sub New(inputBox As TextBox)
_inputBox = inputBox
_burstTimer = New Timer With {.Interval = 80}
AddHandler _burstTimer.Tick, AddressOf OnBurstTimeout
AddHandler _inputBox.KeyPress, AddressOf OnKeyPress
End Sub
Private Sub OnKeyPress(sender As Object, e As KeyPressEventArgs)
If e.KeyChar = ChrW(Keys.Enter) Then
_burstTimer.Stop()
Dim value As String = _buffer.ToString().Trim()
_buffer.Clear()
If value.Length > 0 Then
RaiseEvent BarcodeScanned(Me, value)
End If
Else
_buffer.Append(e.KeyChar)
_burstTimer.Stop()
_burstTimer.Start()
End If
e.Handled = True
End Sub
Private Sub OnBurstTimeout(sender As Object, e As EventArgs)
_burstTimer.Stop()
_buffer.Clear() ' incomplete burst -- discard
End Sub
Public Sub StartListening() Implements IScannerInput.StartListening
_inputBox.Focus()
End Sub
Public Sub StopListening() Implements IScannerInput.StopListening
_inputBox.Enabled = False
End Sub
End Class
Public Class SerialPortScanner
Implements IScannerInput
Public Event BarcodeScanned As EventHandler(Of String) Implements IScannerInput.BarcodeScanned
Private ReadOnly _port As SerialPort
Public Sub New(portName As String, Optional baudRate As Integer = 9600)
_port = New SerialPort(portName, baudRate)
AddHandler _port.DataReceived, AddressOf OnDataReceived
End Sub
Private Sub OnDataReceived(sender As Object, e As SerialDataReceivedEventArgs)
Dim data As String = _port.ReadLine().Trim()
If data.Length > 0 Then
RaiseEvent BarcodeScanned(Me, data)
End If
End Sub
Public Sub StartListening() Implements IScannerInput.StartListening
_port.Open()
End Sub
Public Sub StopListening() Implements IScannerInput.StopListening
_port.Close()
End Sub
End Class
鍵盤楔形實作中的突發計時器是關鍵細節。 每次按鍵都會重置,並且只有在字元停止輸入時才會觸發——這意味著打字速度較慢的真正鍵盤用戶,其未完成的輸入將被丟棄,而不是被當作條碼掃描處理。
如何處理多個掃描器品牌?
企業環境中經常在同一樓層混合運行 Honeywell、Zebra(以前稱為 Symbol/Motorola)和 Datalogic 掃描儀。 每個廠商都有自己的預設終止符、波特率和前綴/後綴約定。 配置模型使您的應用程式具有靈活性:
public class ScannerConfiguration
{
public string ScannerType { get; set; } = "KeyboardWedge";
public string PortName { get; set; } = "COM3";
public int BaudRate { get; set; } = 9600;
public string Terminator { get; set; } = "\r\n";
public bool EnableBeep { get; set; } = true;
public Dictionary<string, string> BrandSettings { get; set; } = new();
public static ScannerConfiguration GetHoneywellConfig() => new()
{
ScannerType = "Serial",
BaudRate = 115200,
BrandSettings = new Dictionary<string, string>
{
{ "Prefix", "STX" },
{ "Suffix", "ETX" },
{ "TriggerMode", "Manual" }
}
};
public static ScannerConfiguration GetZebraConfig() => new()
{
ScannerType = "KeyboardWedge",
BrandSettings = new Dictionary<string, string>
{
{ "ScanMode", "Continuous" },
{ "BeepVolume", "High" }
}
};
}
public class ScannerConfiguration
{
public string ScannerType { get; set; } = "KeyboardWedge";
public string PortName { get; set; } = "COM3";
public int BaudRate { get; set; } = 9600;
public string Terminator { get; set; } = "\r\n";
public bool EnableBeep { get; set; } = true;
public Dictionary<string, string> BrandSettings { get; set; } = new();
public static ScannerConfiguration GetHoneywellConfig() => new()
{
ScannerType = "Serial",
BaudRate = 115200,
BrandSettings = new Dictionary<string, string>
{
{ "Prefix", "STX" },
{ "Suffix", "ETX" },
{ "TriggerMode", "Manual" }
}
};
public static ScannerConfiguration GetZebraConfig() => new()
{
ScannerType = "KeyboardWedge",
BrandSettings = new Dictionary<string, string>
{
{ "ScanMode", "Continuous" },
{ "BeepVolume", "High" }
}
};
}
Option Strict On
Public Class ScannerConfiguration
Public Property ScannerType As String = "KeyboardWedge"
Public Property PortName As String = "COM3"
Public Property BaudRate As Integer = 9600
Public Property Terminator As String = vbCrLf
Public Property EnableBeep As Boolean = True
Public Property BrandSettings As Dictionary(Of String, String) = New Dictionary(Of String, String)()
Public Shared Function GetHoneywellConfig() As ScannerConfiguration
Return New ScannerConfiguration() With {
.ScannerType = "Serial",
.BaudRate = 115200,
.BrandSettings = New Dictionary(Of String, String) From {
{"Prefix", "STX"},
{"Suffix", "ETX"},
{"TriggerMode", "Manual"}
}
}
End Function
Public Shared Function GetZebraConfig() As ScannerConfiguration
Return New ScannerConfiguration() With {
.ScannerType = "KeyboardWedge",
.BrandSettings = New Dictionary(Of String, String) From {
{"ScanMode", "Continuous"},
{"BeepVolume", "High"}
}
}
End Function
End Class
將這些配置儲存在設定檔或資料庫中,意味著倉庫工作人員可以更換掃描器型號而無需重新部署。 ScannerType 欄位決定在啟動時實例化哪個 IScannerInput 實作。
如何在 C# 專案中安裝 IronBarcode?
透過 NuGet 增加 IronBarcode 的最快方法是什麼?
在 Visual Studio 中開啟套件管理員控制台並執行:
Install-Package IronBarCode
Install-Package IronBarCode
或者,使用 .NET CLI:
dotnet add package IronBarCode
dotnet add package IronBarCode
這兩個命令都會從 NuGet.org 取得最新版本,並將組件參考新增到您的專案檔案中。該程式庫面向 .NET Standard 2.0,因此無需任何額外的相容性修補程式即可在 .NET Framework 4.6.2 到 .NET 10 上運行。
安裝完成後,請在呼叫任何 IronBarcode 方法之前設定您的許可證金鑰。 用於開發和評估的免費試用金鑰可從IronBarcode 許可頁面取得:
IronBarCode.License.LicenseKey = "YOUR-LICENSE-KEY";
IronBarCode.License.LicenseKey = "YOUR-LICENSE-KEY";
Imports IronBarCode
IronBarCode.License.LicenseKey = "YOUR-LICENSE-KEY"
對於容器化部署, IronBarcode 可與 Linux 上的 Docker 搭配使用;對於雲端函數,它支援AWS Lambda和Azure Functions 。
如何使用 IronBarcode 驗證掃描的條碼?
格式驗證的正確方法是什麼?
IronBarcode 支援 30 多種條碼符號體系,包括Code 128 、 EAN-13 、 Code 39 、 QR 碼和Data Matrix 。 對於 USB 掃描器應用,驗證模式會將掃描的字串重新編碼為條碼影像,並立即透過解碼器將其讀取回來。 這次往返驗證確認該字串是聲明格式的有效值:
public class BarcodeValidator
{
public async Task<ValidationResult> ValidateAsync(string scannedText, BarcodeEncoding preferredFormat = BarcodeEncoding.Code128)
{
var result = new ValidationResult { RawInput = scannedText };
try
{
var barcode = BarcodeWriter.CreateBarcode(scannedText, preferredFormat);
var readResults = await BarcodeReader.ReadAsync(barcode.ToBitmap());
if (readResults.Any())
{
var first = readResults.First();
result.IsValid = true;
result.Format = first.BarcodeType;
result.Value = first.Value;
result.Confidence = first.Confidence;
}
else
{
result.IsValid = false;
result.Error = "No barcode could be decoded from the scanned input.";
}
}
catch (Exception ex)
{
result.IsValid = false;
result.Error = ex.Message;
}
return result;
}
}
public record ValidationResult
{
public string RawInput { get; init; } = "";
public bool IsValid { get; set; }
public BarcodeEncoding Format { get; set; }
public string Value { get; set; } = "";
public float Confidence { get; set; }
public string Error { get; set; } = "";
}
public class BarcodeValidator
{
public async Task<ValidationResult> ValidateAsync(string scannedText, BarcodeEncoding preferredFormat = BarcodeEncoding.Code128)
{
var result = new ValidationResult { RawInput = scannedText };
try
{
var barcode = BarcodeWriter.CreateBarcode(scannedText, preferredFormat);
var readResults = await BarcodeReader.ReadAsync(barcode.ToBitmap());
if (readResults.Any())
{
var first = readResults.First();
result.IsValid = true;
result.Format = first.BarcodeType;
result.Value = first.Value;
result.Confidence = first.Confidence;
}
else
{
result.IsValid = false;
result.Error = "No barcode could be decoded from the scanned input.";
}
}
catch (Exception ex)
{
result.IsValid = false;
result.Error = ex.Message;
}
return result;
}
}
public record ValidationResult
{
public string RawInput { get; init; } = "";
public bool IsValid { get; set; }
public BarcodeEncoding Format { get; set; }
public string Value { get; set; } = "";
public float Confidence { get; set; }
public string Error { get; set; } = "";
}
Imports System
Imports System.Linq
Imports System.Threading.Tasks
Public Class BarcodeValidator
Public Async Function ValidateAsync(scannedText As String, Optional preferredFormat As BarcodeEncoding = BarcodeEncoding.Code128) As Task(Of ValidationResult)
Dim result As New ValidationResult With {.RawInput = scannedText}
Try
Dim barcode = BarcodeWriter.CreateBarcode(scannedText, preferredFormat)
Dim readResults = Await BarcodeReader.ReadAsync(barcode.ToBitmap())
If readResults.Any() Then
Dim first = readResults.First()
result.IsValid = True
result.Format = first.BarcodeType
result.Value = first.Value
result.Confidence = first.Confidence
Else
result.IsValid = False
result.Error = "No barcode could be decoded from the scanned input."
End If
Catch ex As Exception
result.IsValid = False
result.Error = ex.Message
End Try
Return result
End Function
End Class
Public Class ValidationResult
Public Property RawInput As String = ""
Public Property IsValid As Boolean
Public Property Format As BarcodeEncoding
Public Property Value As String = ""
Public Property Confidence As Single
Public Property Error As String = ""
End Class
對於供應鏈應用中使用的GS1-128 條碼,掃描的字串包含括號中的應用標識符前綴,例如 GTIN 的 (01) 和到期日期的 (17)。 當您指定 BarcodeEncoding.GS1_128 時,IronBarcode 會自動解析這些應用程式識別碼。
開發人員應該實現哪種EAN-13校驗和邏輯?
零售POS應用通常需要在將EAN-13值傳遞給定價查詢之前,獨立驗證EAN-13校驗位。 EAN-13的Luhn式校驗和在前12位數字中交替使用權重1和3:
public static bool ValidateEan13Checksum(string value)
{
if (value.Length != 13 || !value.All(char.IsDigit))
return false;
int sum = 0;
for (int i = 0; i < 12; i++)
{
int digit = value[i] - '0';
sum += (i % 2 == 0) ? digit : digit * 3;
}
int expectedCheck = (10 - (sum % 10)) % 10;
return expectedCheck == (value[12] - '0');
}
public static bool ValidateEan13Checksum(string value)
{
if (value.Length != 13 || !value.All(char.IsDigit))
return false;
int sum = 0;
for (int i = 0; i < 12; i++)
{
int digit = value[i] - '0';
sum += (i % 2 == 0) ? digit : digit * 3;
}
int expectedCheck = (10 - (sum % 10)) % 10;
return expectedCheck == (value[12] - '0');
}
Public Shared Function ValidateEan13Checksum(value As String) As Boolean
If value.Length <> 13 OrElse Not value.All(AddressOf Char.IsDigit) Then
Return False
End If
Dim sum As Integer = 0
For i As Integer = 0 To 11
Dim digit As Integer = AscW(value(i)) - AscW("0"c)
sum += If(i Mod 2 = 0, digit, digit * 3)
Next
Dim expectedCheck As Integer = (10 - (sum Mod 10)) Mod 10
Return expectedCheck = (AscW(value(12)) - AscW("0"c))
End Function
這種純邏輯檢查在編碼之前運行,以避免在高容量零售環境中每次掃描時產生往返影像產生的開銷。 根據GS1 規範,當去掉前導零時,UPC-A(12 位數)的校驗位演算法是相同的。
如何根據掃描的輸入產生響應條碼?
應用程式何時應該在掃描後建立新條碼?
倉庫收貨中常見的模式是"掃描和重新貼標籤"工作流程:入庫物品帶有供應商條碼(通常是 EAN-13 或 ITF-14),倉庫管理系統需要列印一個帶有其自身位置和批號的內部 Code 128 標籤。 IronBarcode 的產生功能只需幾行程式碼即可完成:
public class InventoryLabelGenerator
{
private readonly string _outputDirectory;
public InventoryLabelGenerator(string outputDirectory)
{
_outputDirectory = outputDirectory;
Directory.CreateDirectory(_outputDirectory);
}
public async Task<string> GenerateLabelAsync(string internalCode, string locationCode)
{
string fullCode = $"{internalCode}|{locationCode}|{DateTime.UtcNow:yyyyMMdd}";
// Primary Code 128 label for scanners
var linearBarcode = BarcodeWriter.CreateBarcode(fullCode, BarcodeEncoding.Code128);
linearBarcode.ResizeTo(500, 140);
linearBarcode.SetMargins(12);
linearBarcode.AddAnnotationTextAboveBarcode(fullCode);
linearBarcode.ChangeBarCodeColor(IronSoftware.Drawing.Color.Black);
// QR code companion for mobile apps
var qrCode = BarcodeWriter.CreateQrCode(fullCode);
qrCode.ResizeTo(200, 200);
qrCode.SetMargins(8);
string timestamp = DateTime.UtcNow.ToString("yyyyMMddHHmmss");
string pngPath = Path.Combine(_outputDirectory, $"{internalCode}_{timestamp}.png");
string pdfPath = Path.Combine(_outputDirectory, $"{internalCode}_{timestamp}.pdf");
await Task.Run(() =>
{
linearBarcode.SaveAsPng(pngPath);
linearBarcode.SaveAsPdf(pdfPath);
});
return pngPath;
}
}
public class InventoryLabelGenerator
{
private readonly string _outputDirectory;
public InventoryLabelGenerator(string outputDirectory)
{
_outputDirectory = outputDirectory;
Directory.CreateDirectory(_outputDirectory);
}
public async Task<string> GenerateLabelAsync(string internalCode, string locationCode)
{
string fullCode = $"{internalCode}|{locationCode}|{DateTime.UtcNow:yyyyMMdd}";
// Primary Code 128 label for scanners
var linearBarcode = BarcodeWriter.CreateBarcode(fullCode, BarcodeEncoding.Code128);
linearBarcode.ResizeTo(500, 140);
linearBarcode.SetMargins(12);
linearBarcode.AddAnnotationTextAboveBarcode(fullCode);
linearBarcode.ChangeBarCodeColor(IronSoftware.Drawing.Color.Black);
// QR code companion for mobile apps
var qrCode = BarcodeWriter.CreateQrCode(fullCode);
qrCode.ResizeTo(200, 200);
qrCode.SetMargins(8);
string timestamp = DateTime.UtcNow.ToString("yyyyMMddHHmmss");
string pngPath = Path.Combine(_outputDirectory, $"{internalCode}_{timestamp}.png");
string pdfPath = Path.Combine(_outputDirectory, $"{internalCode}_{timestamp}.pdf");
await Task.Run(() =>
{
linearBarcode.SaveAsPng(pngPath);
linearBarcode.SaveAsPdf(pdfPath);
});
return pngPath;
}
}
Imports System.IO
Imports System.Threading.Tasks
Public Class InventoryLabelGenerator
Private ReadOnly _outputDirectory As String
Public Sub New(outputDirectory As String)
_outputDirectory = outputDirectory
Directory.CreateDirectory(_outputDirectory)
End Sub
Public Async Function GenerateLabelAsync(internalCode As String, locationCode As String) As Task(Of String)
Dim fullCode As String = $"{internalCode}|{locationCode}|{DateTime.UtcNow:yyyyMMdd}"
' Primary Code 128 label for scanners
Dim linearBarcode = BarcodeWriter.CreateBarcode(fullCode, BarcodeEncoding.Code128)
linearBarcode.ResizeTo(500, 140)
linearBarcode.SetMargins(12)
linearBarcode.AddAnnotationTextAboveBarcode(fullCode)
linearBarcode.ChangeBarCodeColor(IronSoftware.Drawing.Color.Black)
' QR code companion for mobile apps
Dim qrCode = BarcodeWriter.CreateQrCode(fullCode)
qrCode.ResizeTo(200, 200)
qrCode.SetMargins(8)
Dim timestamp As String = DateTime.UtcNow.ToString("yyyyMMddHHmmss")
Dim pngPath As String = Path.Combine(_outputDirectory, $"{internalCode}_{timestamp}.png")
Dim pdfPath As String = Path.Combine(_outputDirectory, $"{internalCode}_{timestamp}.pdf")
Await Task.Run(Sub()
linearBarcode.SaveAsPng(pngPath)
linearBarcode.SaveAsPdf(pdfPath)
End Sub)
Return pngPath
End Function
End Class
將文件儲存為 PDF 格式對於可以透過網路共用接收 PDF 輸入的標籤印表機尤其有用。 您也可以匯出為 SVG 格式以獲得向量品質的熱敏標籤輸出,或匯出為位元組流以直接傳送到標籤印表機 API。
IronBarcode 支援廣泛的樣式自訂,包括自訂顏色、邊距調整、人可讀文字疊加,以及為二維碼嵌入徽標,以製作品牌標記的移動標籤。

