跳過到頁腳內容
使用IRONBARCODE

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();
}
$vbLabelText   $csharpLabel

鍵盤楔形實作中的突發計時器是關鍵細節。 每次按鍵都會重置,並且只有在字元停止輸入時才會觸發——這意味著打字速度較慢的真正鍵盤用戶,其未完成的輸入將被丟棄,而不是被當作條碼掃描處理。

如何處理多個掃描器品牌?

企業環境中經常在同一樓層混合運行 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" }
        }
    };
}
$vbLabelText   $csharpLabel

將這些配置儲存在設定檔或資料庫中,意味著倉庫工作人員可以更換掃描器型號而無需重新部署。 ScannerType 欄位決定在啟動時實例化哪個 IScannerInput 實作。

如何在 C# 專案中安裝IronBarcode ?

透過NuGet添加IronBarcode的最快方法是什麼?

在 Visual Studio 中開啟程式包管理器控制台並執行:

Install-Package IronBarCode
Install-Package IronBarCode
SHELL

或者,使用.NET CLI:

dotnet add package IronBarCode
dotnet add package IronBarCode
SHELL

這兩個指令都會從NuGet取得最新版本,並將程式集參考新增到您的專案檔案中。此函式庫面向.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";
$vbLabelText   $csharpLabel

對於容器化部署, IronBarcode可與 Linux 上的 Docker 搭配使用;對於雲端函數,它支援AWS LambdaAzure Functions

如何使用IronBarcode來驗證掃描的條碼?

格式驗證的正確方法是什麼?

IronBarcode支援 30 多種條碼符號體系,包括Code 128EAN-13Code 39QR 碼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; } = "";
}
$vbLabelText   $csharpLabel

對於供應鏈應用中使用的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');
}
$vbLabelText   $csharpLabel

這種純邏輯檢查在編碼之前運行,以避免在高容量零售環境中每次掃描時產生往返影像產生的開銷。 根據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;
    }
}
$vbLabelText   $csharpLabel

將文件儲存為 PDF 格式對於可以透過網路共用接收 PDF 輸入的標籤印表機尤其有用。 您也可以匯出為 SVG 格式以獲得向量品質的熱敏標籤輸出,或匯出為位元組流以直接傳送到標籤印表機 API。

IronBarcode支援廣泛的樣式自訂,包括自訂顏色、邊距調整、人可讀文字疊加,以及為二維碼嵌入徽標,以製作品牌標記的移動標籤。

![Windows Forms 應用程式介面,示範 IronBarcode 的雙條碼產生功能。 介面顯示已成功產生庫存編號為"INV-20250917-helloworld"的 Code 128 線性條碼和二維碼。 頂部的輸入欄位允許使用者輸入自訂庫存代碼,並有一個"產生"按鈕來建立條碼。 成功訊息"專案處理成功 - 已產生標籤"確認操作已完成。 Code 128 條碼被標記為主要的庫存追蹤格式,而下方的二維碼則被標記為行動友善替代方案。該應用程式採用專業的灰色背景和清晰的視覺層次結構,展示了IronBarcode如何幫助開發人員創建多格式條碼生成系統,以實現完整的庫存管理。

如何建立一個完整的高容量掃描應用程式?

基於生產隊列的實作方式是怎樣的?

對於每分鐘處理數十次掃描的應用程式來說,UI 執行緒上的簡單同步處理程序會成為瓶頸。 以下模式使用 ConcurrentQueue<t> 和後台處理循環將掃描擷取與處理解耦。 IronBarcode的非同步 API處理驗證,而不會阻塞使用者介面:

using IronBarCode;
using System.Collections.Concurrent;

public partial class HighVolumeScanner : Form
{
    private readonly ConcurrentQueue<(string Data, DateTime Timestamp)> _scanQueue = new();
    private readonly SemaphoreSlim _semaphore;
    private readonly CancellationTokenSource _cts = new();
    private IScannerInput _scanner;

    public HighVolumeScanner()
    {
        InitializeComponent();
        IronBarCode.License.LicenseKey = "YOUR-LICENSE-KEY";
        _semaphore = new SemaphoreSlim(Environment.ProcessorCount);
        InitializeScanner();
        _ = RunProcessingLoopAsync();
    }

    private void InitializeScanner()
    {
        _scanner = System.IO.Ports.SerialPort.GetPortNames().Any()
            ? new SerialPortScanner("COM3", 115200)
            : new KeyboardWedgeScanner(txtScannerInput);

        _scanner.BarcodeScanned += (_, barcode) =>
            _scanQueue.Enqueue((barcode, DateTime.UtcNow));

        _scanner.StartListening();
    }

    private async Task RunProcessingLoopAsync()
    {
        while (!_cts.Token.IsCancellationRequested)
        {
            if (_scanQueue.TryDequeue(out var scan))
            {
                await _semaphore.WaitAsync(_cts.Token);
                _ = Task.Run(async () =>
                {
                    try { await ProcessScanAsync(scan.Data, scan.Timestamp); }
                    finally { _semaphore.Release(); }
                }, _cts.Token);
            }
            else
            {
                await Task.Delay(10, _cts.Token);
            }
        }
    }

    private async Task ProcessScanAsync(string rawData, DateTime scanTime)
    {
        var options = new BarcodeReaderOptions
        {
            Speed = ReadingSpeed.均衡,
            ExpectMultipleBarcodes = false,
            ExpectBarcodeTypes = BarcodeEncoding.Code128 | BarcodeEncoding.QRCode,
            MaxParallelThreads = 1
        };

        var testBarcode = BarcodeWriter.CreateBarcode(rawData, BarcodeEncoding.Code128);
        var results = await BarcodeReader.ReadAsync(testBarcode.ToBitmap(), options);

        if (results.Any())
        {
            var item = results.First();
            BeginInvoke(() => UpdateInventoryDisplay(item.Value, scanTime));
        }
        else
        {
            BeginInvoke(() => LogRejectedScan(rawData, scanTime));
        }
    }

    protected override void OnFormClosing(FormClosingEventArgs e)
    {
        _cts.Cancel();
        _scanner.StopListening();
        base.OnFormClosing(e);
    }
}
using IronBarCode;
using System.Collections.Concurrent;

public partial class HighVolumeScanner : Form
{
    private readonly ConcurrentQueue<(string Data, DateTime Timestamp)> _scanQueue = new();
    private readonly SemaphoreSlim _semaphore;
    private readonly CancellationTokenSource _cts = new();
    private IScannerInput _scanner;

    public HighVolumeScanner()
    {
        InitializeComponent();
        IronBarCode.License.LicenseKey = "YOUR-LICENSE-KEY";
        _semaphore = new SemaphoreSlim(Environment.ProcessorCount);
        InitializeScanner();
        _ = RunProcessingLoopAsync();
    }

    private void InitializeScanner()
    {
        _scanner = System.IO.Ports.SerialPort.GetPortNames().Any()
            ? new SerialPortScanner("COM3", 115200)
            : new KeyboardWedgeScanner(txtScannerInput);

        _scanner.BarcodeScanned += (_, barcode) =>
            _scanQueue.Enqueue((barcode, DateTime.UtcNow));

        _scanner.StartListening();
    }

    private async Task RunProcessingLoopAsync()
    {
        while (!_cts.Token.IsCancellationRequested)
        {
            if (_scanQueue.TryDequeue(out var scan))
            {
                await _semaphore.WaitAsync(_cts.Token);
                _ = Task.Run(async () =>
                {
                    try { await ProcessScanAsync(scan.Data, scan.Timestamp); }
                    finally { _semaphore.Release(); }
                }, _cts.Token);
            }
            else
            {
                await Task.Delay(10, _cts.Token);
            }
        }
    }

    private async Task ProcessScanAsync(string rawData, DateTime scanTime)
    {
        var options = new BarcodeReaderOptions
        {
            Speed = ReadingSpeed.均衡,
            ExpectMultipleBarcodes = false,
            ExpectBarcodeTypes = BarcodeEncoding.Code128 | BarcodeEncoding.QRCode,
            MaxParallelThreads = 1
        };

        var testBarcode = BarcodeWriter.CreateBarcode(rawData, BarcodeEncoding.Code128);
        var results = await BarcodeReader.ReadAsync(testBarcode.ToBitmap(), options);

        if (results.Any())
        {
            var item = results.First();
            BeginInvoke(() => UpdateInventoryDisplay(item.Value, scanTime));
        }
        else
        {
            BeginInvoke(() => LogRejectedScan(rawData, scanTime));
        }
    }

    protected override void OnFormClosing(FormClosingEventArgs e)
    {
        _cts.Cancel();
        _scanner.StopListening();
        base.OnFormClosing(e);
    }
}
$vbLabelText   $csharpLabel

SemaphoreSlim 將並發驗證任務限制為邏輯處理器的數量,防止在突發掃描事件期間建立失控的執行緒。 BeginInvoke 呼叫安全地將 UI 更新序列化回主執行緒。

如何針對不同的掃描範圍調整效能?

BarcodeReaderOptions.Speed 屬性接受 ReadingSpeed.均衡ReadingSpeed.詳細的。 對於已知字串值的 USB 掃描器輸​​入,均衡 是適當的-解碼器只需要確認格式,不需要在影像中尋找條碼。 根據IronBarcode 的讀取速度文檔快點 模式會跳過一些失真校正演算法,這對於乾淨的掃描器輸出來說是安全的,但在基於影像的場景中可能會漏掉損壞的條碼。

下表總結了每種速度模式的使用時機:

IronBarcode讀取速度模式及其適用場景
速度模式 最適合 權衡
快點 清潔的USB掃描器輸入,高吞吐量 可能漏檢嚴重損壞或傾斜的條碼
均衡 混合輸入-USB掃描器加影像導入 CPU佔用率適中,準確率高
詳細的 標籤破損、列印對比度低、PDF導入 CPU 使用率高,吞吐量最慢

對於除了 USB 掃描器輸​​入外,還處理影像或 PDF 的應用程序,IronBarcode 可以使用相同的 API 介面從 PDF 文件和多頁 TIFF 檔案IronBarcode讀取條碼

![專業 Windows Forms 條碼掃描器應用程序,展示了 IronBarcode 的即時庫存追蹤功能。 介面採用簡潔的雙面板設計,搭配精緻的深藍色標題列。 左側面板顯示掃描歷史記錄列表,其中顯示了四個成功掃描的庫存項目(INV-001 至 INV-004),並帶有精確的時間戳和掃描狀態指示器。 每個項目都包含詳細的元數據,例如條碼類型和置信度。 右側面板顯示動態產生的摘要條碼,其中顯示"商品數量:4",並具有專業的樣式和適當的邊距。 底部的操作按鈕包括"清除清單"、"匯出資料"和"列印標籤",用於完整的庫存管理。 狀態列顯示"掃描器:已連線" | 模式:連續 | 上次掃描時間:2 秒前,這展示了IronBarcode為生產庫存系統提供的即時監控功能和專業的企業級設計。

如何處理掃描器應用程式中的極端情況和錯誤?

開發人員應該預見哪些故障模式?

USB掃描器應用程式的故障方式是可以預見的。 最常見的問題及其緩解措施如下:

掃描器斷開連接-當 USB 掃描器拔出時,鍵盤楔形文字方塊將失去其虛擬鍵盤。 最簡單的緩解措施是使用週期性定時器來檢查 _inputBox.Focused,如果掃描器仍然列在已連接的 HID 設備中,則重新聚焦它。 對於串行掃描儀,SerialPort.GetPortNames() 檢測重新連接。

條碼格式不明確-有些產品帶有條碼,這些條碼在多種條碼格式中均有效。 例如,一個 12 位元數字字串既是有效的 UPC-A,也是有效的 Code 128。在 BarcodeReaderOptions 中指定 ExpectBarcodeTypes 可以將解碼器限制為預期格式,並消除歧義。 IronBarcode故障排除指南涵蓋了特定格式的識別技巧。

無效格式異常-- 如果 BarcodeWriter.CreateBarcode 接收到違反所選編碼規則的字串(例如,在僅包含數字的 EAN-13 欄位中包含字母字元),則會拋出 IronBarCode.Exceptions.InvalidBarcodeException 例外。 將呼叫包裝在 try-catch 語句中,並回退到僅字串驗證路徑,可以保持應用程式運行。

按鍵時間衝突-在操作員也手動向同一個文字方塊輸入內容的環境中,前面所描述的突發計時器方法是主要的防禦措施。 二級保護是最小長度:大多數真正的條碼至少有 8 個字符,因此短於此長度的字串可以視為鍵盤輸入。

在排查串行掃描器連接問題時,Microsoft .NET文件中關於System.IO.Ports.SerialPort 的內容非常有用,特別是關於 ReadTimeoutWriteTimeout 設定的問題。 為了符合零售業的監管規定, GS1 通用規範為每個應用識別碼定義了有效值範圍。

如何將應用程式擴展到行動和網路平台?

上圖所示的掃描器介面模式 -- IScannerInputBarcodeScanned 事件 -- 將硬體與處理邏輯隔離。 交換實作方式可以讓相同的驗證和產生程式碼在不同的平台上運作:

  • .NET MAUI為用作行動接收站的 Android 和 iOS 平板電腦提供基於相機的掃描器實作。 Blazor伺服器支援基於瀏覽器的掃描,透過JavaScript存取相機,並將資料輸入到同一個 BarcodeScanned 事件中。
  • AndroidiOS原生實作為行動開發者提供了相機掃描功能,後端使用相同的IronBarcode解碼器。

對於雲端原生架構,驗證和標籤產生步驟可以作為Azure Functions運行,由佇列訊息觸發,而桌面應用程式僅充當掃描器輸入網關。 當需要集中管理標籤列印邏輯以進行合規性審核時,這種分離方式尤其有用。

下一步計劃是什麼?

使用IronBarcode建立 USB 條碼掃描器應用程式涉及四個特定階段:透過突發時序偵測擷取鍵盤楔形輸入、透過 IronBarcode 的解碼器驗證掃描值、產生所需格式的回應標籤以及使用並發佇列處理大量掃描。 每個階段都是獨立的,可以單獨進行測試。

在此基礎上,可以考慮擴展應用程序,例如為批量處理場景添加多條碼讀取功能、為基於圖像的輸入添加作物區域優化功能,或者為較舊的倉庫設備添加MSI 條碼支援功能IronBarcode文件涵蓋了所有支援的格式和進階讀取器配置選項。

立即開始免費試用,取得開發許可證金鑰,並開始將IronBarcode整合到您的掃描應用程式中。

常見問題解答

什麼是 IronBarcode,它與 USB 條碼掃描器有何關係?

IronBarcode 是一個函式庫,使開發人員能夠構建用於 USB 條碼掃描的強大 C# 應用程式。它提供了條碼驗證、數據提取和條碼生成等功能。

IronBarcode 可以驗證來自 USB 掃描器的條碼數據嗎?

是的,IronBarcode 可以驗證從 USB 掃描器截取的條碼數據,確保 C# 應用程式中的數據完整性和準確性。

IronBarcode 如何處理條碼生成?

IronBarcode 可以即時生成新的條碼,使開發人員能夠在其 C# 應用程式中輕松創建和打印條碼。

IronBarcode 是否支持 USB 條碼掃描的錯誤處理?

是的,IronBarcode 包含全面的錯誤處理,以管理 USB 條碼掃描和處理過程中可能出現的常見問題。

IronBarcode 可以掃描哪些類型的條碼?

IronBarcode 支持掃描各種條碼符號,包括 QR 碼、UPC、Code 39 等,這使其在各種應用中具有多功能性。

IronBarcode 能夠提取掃描條碼中的結構化信息嗎?

是的,IronBarcode 可以從掃描的條碼中提取結構化信息,協助高效數據處理和管理。

我如何開始在 C# 中構建 USB 條碼掃描器應用程式?

要開始在 C# 中構建 USB 條碼掃描器應用程序,您可以利用 IronBarcode,並結合提供的代碼示例和文檔來指導您的開發過程。

Jordi Bardia
軟體工程師
Jordi 在 Python、C# 和 C++ 上最得心應手,當他不在 Iron Software 展現技術時,便在做遊戲編程。在分担產品测测试,產品開發和研究的责任時,Jordi 為持续的產品改進增值。他说这种多样化的经验使他受到挑战并保持参与, 而这也是他与 Iron Software 中工作一大乐趣。Jordi 在佛罗里达州迈阿密长大,曾在佛罗里达大学学习计算机科学和统计学。

鋼鐵支援團隊

我們每週 5 天,每天 24 小時在線上。
聊天
電子郵件
打電話給我