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();
}
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
$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" }
        }
    };
}
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
$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";
Imports IronBarCode

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; } = "";
}
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
$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');
}
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
$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;
    }
}
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
$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);
    }
}
Imports IronBarCode
Imports System.Collections.Concurrent
Imports System.Threading
Imports System.Threading.Tasks

Public Partial Class HighVolumeScanner
    Inherits Form

    Private ReadOnly _scanQueue As New ConcurrentQueue(Of (Data As String, Timestamp As DateTime))()
    Private ReadOnly _semaphore As SemaphoreSlim
    Private ReadOnly _cts As New CancellationTokenSource()
    Private _scanner As IScannerInput

    Public Sub New()
        InitializeComponent()
        IronBarCode.License.LicenseKey = "YOUR-LICENSE-KEY"
        _semaphore = New SemaphoreSlim(Environment.ProcessorCount)
        InitializeScanner()
        _ = RunProcessingLoopAsync()
    End Sub

    Private Sub InitializeScanner()
        _scanner = If(System.IO.Ports.SerialPort.GetPortNames().Any(),
                      New SerialPortScanner("COM3", 115200),
                      New KeyboardWedgeScanner(txtScannerInput))

        AddHandler _scanner.BarcodeScanned, Sub(_, barcode)
                                                _scanQueue.Enqueue((barcode, DateTime.UtcNow))
                                            End Sub

        _scanner.StartListening()
    End Sub

    Private Async Function RunProcessingLoopAsync() As Task
        While Not _cts.Token.IsCancellationRequested
            Dim scan As (Data As String, Timestamp As DateTime)
            If _scanQueue.TryDequeue(scan) Then
                Await _semaphore.WaitAsync(_cts.Token)
                _ = Task.Run(Async Function()
                                 Try
                                     Await ProcessScanAsync(scan.Data, scan.Timestamp)
                                 Finally
                                     _semaphore.Release()
                                 End Try
                             End Function, _cts.Token)
            Else
                Await Task.Delay(10, _cts.Token)
            End If
        End While
    End Function

    Private Async Function ProcessScanAsync(rawData As String, scanTime As DateTime) As Task
        Dim options As New BarcodeReaderOptions With {
            .Speed = ReadingSpeed.Equilibrado,
            .ExpectMultipleBarcodes = False,
            .ExpectBarcodeTypes = BarcodeEncoding.Code128 Or BarcodeEncoding.QRCode,
            .MaxParallelThreads = 1
        }

        Dim testBarcode = BarcodeWriter.CreateBarcode(rawData, BarcodeEncoding.Code128)
        Dim results = Await BarcodeReader.ReadAsync(testBarcode.ToBitmap(), options)

        If results.Any() Then
            Dim item = results.First()
            BeginInvoke(Sub() UpdateInventoryDisplay(item.Value, scanTime))
        Else
            BeginInvoke(Sub() LogRejectedScan(rawData, scanTime))
        End If
    End Function

    Protected Overrides Sub OnFormClosing(e As FormClosingEventArgs)
        _cts.Cancel()
        _scanner.StopListening()
        MyBase.OnFormClosing(e)
    End Sub
End Class
$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

Equipe de suporte de ferro

Estamos online 24 horas por dia, 5 dias por semana.
Bater papo
E-mail
Liga para mim