Ir para o conteúdo do rodapé
USANDO O IRONBARCODE

Leitor de código de barras USB em C#: Crie um aplicativo de leitura completo

Scanners de código de barras USB conectam-se a aplicativos C# como dispositivos padrão de entrada do teclado, enviando os dados digitalizados como caracteres digitados seguidos por uma tecla Enter. Esse comportamento de teclado em modo HID torna a integração simples -- seu aplicativo recebe entrada de texto sem necessidade de driver especial ou SDK. IronBarcode processa essa entrada bruta para validar formatos, extrair dados estruturados e gerar códigos de barras de resposta, transformando um simples evento de digitalização em um pipeline de dados completo para gestão de inventário, ponto de venda no varejo e sistemas de rastreamento de logística.

Operações de varejo, armazenamento e fabricação dependem de digitalização precisa e rápida de códigos de barras. Quando um desenvolvedor conecta um scanner USB a um aplicativo Windows Forms ou WPF, o scanner se comporta idêntico a um teclado -- os dados chegam em um TextBox e pressionar Enter sinaliza que um código de barras completo foi recebido. O desafio não é capturar os dados; é processá-los corretamente. A validação de códigos de barras do IronBarcode verifica a integridade do formato, extrai campos como números de lote ou identificadores de aplicativo GS1, e pode imediatamente gerar um novo código de barras em resposta.

Este guia percorre passo a passo a construção de um aplicativo de scanner de código de barras USB pronto para produção em C#. Você instalará a biblioteca, capturará a entrada do scanner, validará formatos de códigos de barras, gerará etiquetas de resposta e montará um processador com base em filas de alto volume. Cada seção inclui código completo e executável direcionado a .NET 10 com estilo de declaração de nível superior, onde apropriado.

Como Funcionam os Scanners de Código de Barras USB com C#?

Por Que o Modo de Teclado HID Facilita a Integração?

A maioria dos scanners de código de barras USB vem configurada em modo de teclado HID por padrão. Quando você conecta um em uma máquina Windows, o sistema operacional o registra como um dispositivo de armazenamento USB (para configuração) e um teclado (para entrada de dados). Quando um código de barras é digitalizado, o dispositivo traduz o valor decodificado do código de barras em pressionamentos de teclas e os envia para qualquer janela de aplicativo que esteja em foco, adicionando um retorno de carro no final.

Do ponto de vista de um desenvolvedor C#, isso significa que você não precisa de SDKs de fornecedores, bibliotecas COM ou APIs USB especiais. Um TextBox padrão com um manipulador KeyDown é tudo o que é necessário para capturar a entrada. O principal desafio de integração é distinguir a entrada do scanner da digitação genuína do teclado. Os scanners normalmente entregam todos os caracteres em uma explosão muito curta -- muitas vezes abaixo de 50 milissegundos -- enquanto a digitação humana espalha os pressionamentos de teclas por centenas de milissegundos. Cronometrar a explosão é uma maneira confiável de filtrar pressionamentos de teclas acidentais.

Scanners de grau profissional também suportam modos seriais (RS-232 ou porta COM virtual) e USB HID diretos, que dão mais controle sobre caracteres de prefixo/sufixo e gatilhos de digitalização. O padrão de interface abaixo lida com ambos os casos:

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

O cronômetro de explosão na implementação de teclado HID é o detalhe chave. Ele é redefinido a cada pressionamento de tecla e somente dispara se os caracteres pararem de chegar -- significando que usuários de teclado genuínos que digitam devagar terão sua entrada incompleta descartada em vez de tratada como uma digitalização de código de barras.

Como Você Lida com Múltiplas Marcas de Scanners?

Ambientes corporativos frequentemente operam uma mistura de scanners Honeywell, Zebra (anteriormente Symbol/Motorola) e Datalogic no mesmo piso. Cada fornecedor tem seus próprios caracteres de término padrão, taxas de baud e convenções de prefixo/sufixo. Um modelo de configuração mantém seu aplicativo flexível:

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

Armazenar essas configurações em um arquivo de configurações ou banco de dados significa que o pessoal do armazém pode trocar os modelos de scanners sem necessitar de uma redistribuição. O campo ScannerType determina qual implementação IScannerInput é instanciada na inicialização.

Como Se Instala o IronBarcode em um Projeto C#?

Qual É a Maneira Mais Rápida de Adicionar IronBarcode via NuGet?

Abra o Console do Gerenciador de Pacotes no Visual Studio e execute:

Install-Package IronBarCode
Install-Package IronBarCode
SHELL

Alternativamente, use o .NET CLI:

dotnet add package IronBarCode
dotnet add package IronBarCode
SHELL

Ambos os comandos puxam a versão atual do NuGet.org e adicionam a referência do assembly ao seu arquivo de projeto. A biblioteca visa o .NET Standard 2.0, portanto, funciona em .NET Framework 4.6.2 até .NET 10 sem necessidade de shims de compatibilidade adicionais.

Após instalar, defina sua chave de licença antes de chamar qualquer método IronBarcode. Para desenvolvimento e avaliação, uma chave de teste gratuita está disponível na página de licenciamento IronBarcode:

IronBarCode.License.LicenseKey = "YOUR-LICENSE-KEY";
IronBarCode.License.LicenseKey = "YOUR-LICENSE-KEY";
$vbLabelText   $csharpLabel

Para implantações containerizadas, IronBarcode funciona com Docker no Linux, e para funções de nuvem, suporta AWS Lambda e Azure Functions.

Como Você Valida Códigos de Barras Digitalizados com IronBarcode?

Qual É a Abordagem Correta para Verificação de Formato?

IronBarcode suporta mais de 30 simbologias de códigos de barras, incluindo Code 128, EAN-13, Code 39, códigos QR e Data Matrix. Para aplicações com scanner USB, o padrão de validação re-codifica a string digitalizada como uma imagem de código de barras e a lê imediatamente de volta através do decodificador. Este ciclo confirma que a string é um valor válido para o formato declarado:

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

Para códigos de barras GS1-128 usados em aplicações de cadeia de suprimentos, a string escaneada inclui prefixos identificadores de aplicação entre parênteses, como (01) para GTIN e (17) para data de validade. IronBarcode analisa automaticamente esses identificadores de aplicação quando você especifica BarcodeEncoding.GS1_128.

Que Lógica de Verificação de Dígitos de Controle EAN-13 Os Desenvolvedores Devem Implementar?

Aplicações de ponto de venda no varejo frequentemente precisam verificar dígitos de controle EAN-13 independentemente antes de passar o valor para uma pesquisa de preços. O checksum estilo Luhn para EAN-13 alterna pesos de 1 e 3 nos primeiros 12 dígitos:

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

Esta verificação puramente lógica é executada antes da codificação para evitar a sobrecarga de geração de imagens em ciclo para cada digitalização em um ambiente de varejo de alto volume. De acordo com a especificação GS1, o algoritmo de dígito de controle é idêntico para UPC-A (12 dígitos) quando você remove o zero inicial.

Como Você Gera Códigos de Barras de Resposta a partir de Entradas Digitalizadas?

Quando um Aplicativo Deve Criar Novos Códigos de Barras Após uma Digitalização?

Um padrão comum no recebimento de armazém é o fluxo de trabalho "digitalizar e re-etiquetar": um item recebido traz um código de barras do fornecedor (comumente EAN-13 ou ITF-14), e o sistema de gestão de armazém precisa imprimir uma etiqueta interna Code 128 com seus próprios códigos de local e lote. As capacidades de geração do IronBarcode lidam com isso em poucas linhas:

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

Salvar como PDF é especialmente útil para impressoras de etiquetas que aceitam entrada PDF através de compartilhamento de rede. Você também pode exportar como SVG para saída de etiqueta térmica de qualidade vetorial, ou exportar como um fluxo de bytes para enviar diretamente para uma API de impressora de etiquetas.

IronBarcode suporta customização extensa de estilo, incluindo cores personalizadas, ajustes de margem, sobreposições de texto legível por humanos e, para códigos QR, incorporação de logotipo para etiquetas móveis marcadas por marca.

Interface do aplicativo Windows Forms demonstrando as capacidades duplas de geração de código de barras do IronBarcode. A interface mostra a geração bem-sucedida de um código de barras linear Code 128 e de um código QR para o número de inventário 'INV-20250917-helloworld'. O campo de entrada na parte superior permite que os usuários insiram códigos de inventário personalizados, com um botão 'Gerar' para criar os códigos de barras. A mensagem de sucesso 'Item processado com sucesso - Etiquetas Geradas' confirma que a operação foi concluída. O código de barras Code 128 é rotulado como o formato principal de rastreamento de inventário, enquanto o código QR abaixo é marcado como uma alternativa amigável ao celular. O aplicativo utiliza um fundo cinza profissional com hierarquia visual clara, demonstrando como o IronBarcode permite que os desenvolvedores criem sistemas de geração de códigos de barras multiformatos para gestão completa de inventário.

Como Construir um Aplicativo de Escaneamento de Alto Volume Completo?

Como é uma Implementação Baseada em Fila de Produção?

Para aplicativos que processam dezenas de escaneamentos por minuto, um manipulador síncrono simples na thread da interface se torna um gargalo. O padrão abaixo desacopla a captura de escaneamento do processamento usando um ConcurrentQueue<t> e um loop de processamento em segundo plano. A API assíncrona do IronBarcode lida com a validação sem bloquear a interface do usuário:

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.Equilibrado,
            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.Equilibrado,
            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

O SemaphoreSlim limita tarefas de validação concorrente ao número de processadores lógicos, evitando a criação descontrolada de threads durante eventos de escaneamento em massa. As chamadas BeginInvoke gerenciam atualizações de UI de volta à thread principal de forma segura.

Como Ajustar o Desempenho para Diferentes Volumes de Escaneamento?

A propriedade BarcodeReaderOptions.Speed aceita ReadingSpeed.Mais Rápido, ReadingSpeed.Equilibrado e ReadingSpeed.Detalhado. Para entrada de scanner USB onde o valor da string já é conhecido, Equilibrado é apropriado -- o decodificador apenas precisa confirmar o formato, não localizar um código de barras em uma imagem. De acordo com a documentação de velocidade de leitura do IronBarcode, o modo Mais Rápido ignora alguns algoritmos de correção de distorção, o que é seguro para saída limpa de scanners mas pode perder códigos de barras danificados em cenários baseados em imagens.

A tabela a seguir resume quando usar cada modo de velocidade:

Modos de velocidade de leitura do IronBarcode e seus casos de uso apropriados
Modo de Velocidade Ideal para Compensação
Mais Rápido Entrada de scanner USB limpa, alto volume de produção Pode perder códigos de barras muito danificados ou inclinados
Equilibrado Entrada mista -- scanner USB mais importações de imagem Uso moderado de CPU, boa precisão
Detalhado Etiquetas danificadas, impressão de baixo contraste, importações de PDF Maior uso de CPU, menor produtividade

Para aplicativos que também processam imagens ou PDFs junto com entrada de scanner USB, IronBarcode pode ler códigos de barras de documentos PDF e arquivos TIFF de várias páginas usando a mesma superfície de API.

Aplicativo profissional de scanner de código de barras do Windows Forms demostrando as capacidades de rastreamento de inventário em tempo real do IronBarcode. A interface apresenta um design limpo de dois painéis com um sofisticado cabeçalho azul escuro. O painel esquerdo exibe uma lista de histórico de escaneamento mostrando quatro itens de inventário escaneados com sucesso (INV-001 a INV-004) com timestamps precisos e indicadores de status de escaneamento. Cada item inclui metadados detalhados, como tipo de código de barras e nível de confiança. O painel direito mostra um código de barras resumo gerado dinamicamente exibindo 'Itens: 4' com estilo profissional e margens apropriadas. Botões de ação na parte inferior incluem 'Limpar Lista', 'Exportar Dados' e 'Imprimir Etiquetas' para gestão completa de inventário. A barra de status indica 'Scanner: Conectado | Modo: Contínuo | Último Escaneamento: 2 segundos atrás', demonstrando as capacidades de monitoramento em tempo real do aplicativo e design pronto para empresas que o IronBarcode capacita para sistemas de inventário em produção.

Como Lidar com Casos de Borda e Erros em Aplicativos de Scanner?

Quais Modos de Falha os Desenvolvedores Devem Antecipar?

Aplicativos de scanner USB falham de maneiras previsíveis. Os problemas mais comuns e suas mitigações são:

Desconexão do scanner -- Quando um scanner USB é desconectado, o TextBox da cunha do teclado perde seu teclado virtual. A mitigação mais simples é um temporizador periódico que verifica _inputBox.Focused e o refoca se o scanner ainda estiver listado em dispositivos HID conectados. Para scanners seriais, SerialPort.GetPortNames() detecta reconexão.

Formatos de código de barras ambíguos -- Alguns produtos possuem códigos de barras válidos em múltiplas simbologias. Por exemplo, uma string de 12 dígitos é um UPC-A válido e também um Código 128 válido. Especificar ExpectBarcodeTypes em BarcodeReaderOptions limita o decodificador aos seus formatos esperados e elimina a ambiguidade. O guia de solução de problemas do IronBarcode cobre dicas de reconhecimento específicas para cada formato.

Exceções de formato inválido -- Se BarcodeWriter.CreateBarcode receber uma string que viole as regras da codificação selecionada (por exemplo, caracteres alfabéticos em um campo EAN-13 numérico), ele lança um IronBarCode.Exceptions.InvalidBarcodeException. Envolver a chamada em um try-catch e recorrer a um caminho de validação apenas de string mantém o aplicativo funcionando.

Colisões de tempo de pressionamento de tecla -- Em ambientes onde os operadores também digitam manualmente no mesmo TextBox, a abordagem de temporizador de explosão descrita anteriormente é a principal defesa. Uma proteção secundária é o comprimento mínimo: a maioria dos códigos de barras reais tem pelo menos 8 caracteres, então cadeias de caracteres mais curtas que isso podem ser tratadas como entrada de teclado.

A documentação da Microsoft .NET sobre System.IO.Ports.SerialPort é útil ao solucionar problemas de conectividade de scanner serial, particularmente em relação às configurações ReadTimeout e WriteTimeout. Para conformidade regulatória no varejo, as Especificações Gerais GS1 definem faixas de valores válidos para cada identificador de aplicação.

Como você estende o aplicativo para plataformas móveis e web?

O padrão de interface do scanner mostrado acima -- IScannerInput com o evento BarcodeScanned -- abstrai o hardware da lógica de processamento. Trocar implementações permite que o mesmo código de validação e geração seja executado em diferentes plataformas:

  • .NET MAUI oferece uma implementação de scanner baseada em câmera para tablets Android e iOS usados como estações móveis de recebimento
  • Blazor Server suporta escaneamento baseado em navegador com acesso a câmera via JavaScript alimentando no mesmo evento BarcodeScanned
  • Implementações nativas do Android e iOS oferecem aos desenvolvedores móveis a capacidade de digitalizar com a câmera usando o mesmo decodificador IronBarcode no backend

Para arquiteturas nativas de nuvem, as etapas de validação e geração de etiquetas podem ser executadas como Azure Functions acionadas por mensagens de fila, com o aplicativo desktop atuando apenas como a porta de entrada de entrada do scanner. Essa separação é particularmente útil quando a lógica de impressão de etiquetas deve ser centralizada para auditoria de conformidade.

Quais são os seus próximos passos?

Construir um aplicativo de scanner de código de barras USB com IronBarcode envolve quatro etapas concretas: capturar a entrada do teclado wedge com detecção de tempo de rajada, validar o valor digitalizado através do decodificador do IronBarcode, gerar etiquetas de resposta no formato necessário e processar altos volumes de digitalização com uma fila concorrente. Cada etapa é independente e testável isoladamente.

A partir daqui, considere estender o aplicativo com leitura de múltiplos códigos de barras para cenários de processamento em lote, otimização de região de corte para entradas baseadas em imagem, ou suporte para código de barras MSI para equipamentos de armazém mais antigos. A documentação do IronBarcode cobre todos os formatos suportados e opções avançadas de configuração de leitor.

Inicie uma avaliação gratuita para obter uma chave de licença de desenvolvimento e começar a integrar o IronBarcode em seu aplicativo de digitalização hoje.

Perguntas frequentes

O que é o IronBarcode e qual a sua relação com os leitores de código de barras USB?

IronBarcode é uma biblioteca que permite aos desenvolvedores criar aplicações robustas em C# para leitura de códigos de barras via USB. Ela oferece recursos como validação de código de barras, extração de dados e geração de códigos de barras.

O IronBarcode consegue validar dados de código de barras provenientes de um leitor USB?

Sim, o IronBarcode pode validar dados de código de barras capturados por um leitor USB, garantindo a integridade e a precisão dos dados em seus aplicativos C#.

Como o IronBarcode lida com a geração de códigos de barras?

O IronBarcode pode gerar novos códigos de barras instantaneamente, permitindo que os desenvolvedores criem e imprimam códigos de barras facilmente em seus aplicativos C#.

O IronBarcode oferece suporte ao tratamento de erros para leitura de códigos de barras via USB?

Sim, o IronBarcode inclui um sistema abrangente de tratamento de erros para lidar com problemas comuns que podem surgir durante a leitura e o processamento de códigos de barras via USB.

Que tipos de códigos de barras podem ser lidos usando o IronBarcode?

O IronBarcode suporta a leitura de uma ampla variedade de simbologias de código de barras, incluindo códigos QR, UPC, Code 39 e muito mais, tornando-o versátil para diversas aplicações.

O IronBarcode consegue extrair informações estruturadas de códigos de barras escaneados?

Sim, o IronBarcode consegue extrair informações estruturadas de códigos de barras digitalizados, auxiliando no processamento e gerenciamento eficiente de dados.

Como posso começar a desenvolver um aplicativo de leitor de código de barras USB em C#?

Para começar a criar um aplicativo de leitor de código de barras USB em C#, você pode utilizar o IronBarcode juntamente com os exemplos de código e a documentação fornecidos para orientar seu processo de desenvolvimento.

Curtis Chau
Redator Técnico

Curtis Chau é bacharel em Ciência da Computação (Universidade Carleton) e se especializa em desenvolvimento front-end, com experiência em Node.js, TypeScript, JavaScript e React. Apaixonado por criar interfaces de usuário intuitivas e esteticamente agradáveis, Curtis gosta de trabalhar com frameworks modernos e criar manuais ...

Leia mais

Iron Support Team

We're online 24 hours, 5 days a week.
Chat
Email
Call Me