Passer au contenu du pied de page
UTILISATION DE IRONBARCODE

Scanner de codes-barres USB en C# : Créez une application de numérisation complète

Les lecteurs de codes-barres USB se connectent aux applications C# comme des périphériques d'entrée clavier standard, envoyant les données scannées sous forme de caractères saisis suivis d'une pression sur la touche Entrée. Ce comportement de ce clavier HID simplifie l'intégration : votre application reçoit les entrées de texte sans qu'aucun pilote ou SDK spécial ne soit requis. IronBarcode traite ces données brutes pour valider les formats, extraire les données structurées et générer des codes-barres de réponse, transformant ainsi un simple événement de numérisation en un pipeline de données complet pour la gestion des stocks, les points de vente au détail et les systèmes de suivi logistique.

Les activités de vente au détail, d'entreposage et de fabrication dépendent toutes d'une lecture rapide et précise des codes-barres. Lorsqu'un développeur connecte un scanner USB à une application Windows Forms ou WPF, le scanner se comporte de la même manière qu'un clavier : les données arrivent dans une zone de texte, et appuyer sur Entrée signale qu'un code-barres complet a été reçu. Le défi n'est pas de recueillir les données ; Le traitement se déroule correctement. Le système de validation de codes-barres d'IronBarcode vérifie l'intégrité du format, extrait des champs tels que les numéros de lot ou les identifiants d'application GS1 et peut générer immédiatement un nouveau code-barres en réponse.

Ce guide vous accompagne pas à pas dans la création d'une application de lecture de codes-barres USB en C# prête pour la production. Vous installerez la bibliothèque, capturerez les données du scanner, validerez les formats de codes-barres, générerez les étiquettes de réponse et assemblerez un processeur à file d'attente pour un volume élevé de données. Chaque section comprend un code complet et exécutable ciblant .NET 10 avec un style d'instruction de niveau supérieur lorsque cela est approprié.

Comment fonctionnent les lecteurs de codes-barres USB avec C# ?

Pourquoi le mode Wedge du clavier HID simplifie-t-il l'intégration ?

La plupart des lecteurs de codes-barres USB sont livrés configurés par défaut en mode clavier HID. Lorsque vous branchez un tel appareil sur un ordinateur Windows, le système d'exploitation l'enregistre à la fois comme un périphérique de stockage USB (pour la configuration) et comme un clavier (pour la saisie de données). Lorsqu'un code-barres est scanné, l'appareil traduit la valeur décodée du code-barres en frappes de clavier et les envoie à la fenêtre d'application qui a le focus, en ajoutant un retour chariot à la fin.

Du point de vue d'un développeur C#, cela signifie qu'il n'est pas nécessaire d'utiliser les kits de développement logiciel (SDK) des fournisseurs, les bibliothèques COM ou les API USB spécifiques. Un simple contrôle TextBox avec un gestionnaire d'événement KeyDown suffit pour la saisie. Le principal défi d'intégration consiste à distinguer les données saisies par scanner de la véritable saisie au clavier. Les scanners délivrent généralement tous les caractères en un laps de temps très court — souvent moins de 50 millisecondes — tandis que la frappe humaine répartit les frappes sur des centaines de millisecondes. Le déclenchement temporel de la rafale est un moyen fiable de filtrer les frappes accidentelles.

Les scanners de qualité professionnelle prennent également en charge les modes série (RS-232 ou port COM virtuel) et USB HID direct, ce qui vous offre un meilleur contrôle sur les caractères de préfixe/suffixe et les déclencheurs de numérisation. Le modèle d'interface ci-dessous gère les deux cas :

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

Le minuteur de rafale intégré au clavier est un détail essentiel. Il se réinitialise à chaque frappe et ne se déclenche que si aucun caractère n'est enregistré, ce qui signifie que les utilisateurs de clavier qui tapent lentement verront leurs saisies incomplètes rejetées plutôt que traitées comme un scan de code-barres.

Comment gérez-vous plusieurs marques de scanners ?

Dans les environnements Enterprise , on trouve fréquemment un mélange de scanners Honeywell, Zebra (anciennement Symbol/Motorola) et Datalogic sur le même étage. Chaque fournisseur possède ses propres caractères de terminaison par défaut, ses débits de transmission et ses conventions de préfixe/suffixe. Un modèle de configuration assure la flexibilité de votre application :

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

Le stockage de ces configurations dans un fichier de paramètres ou une base de données permet au personnel de l'entrepôt de changer de modèle de scanner sans nécessiter de redéploiement. Le champ ScannerType détermine quelle implémentation IScannerInput est instanciée au démarrage.

Comment installer IronBarcode dans un projet C# ?

Quelle est la méthode la plus rapide pour ajouter IronBarcode via NuGet?

Ouvrez la console du Package Manager dans Visual Studio et exécutez :

Install-Package IronBarCode
Install-Package IronBarCode
SHELL

Vous pouvez également utiliser l'interface de ligne de commande .NET :

dotnet add package IronBarCode
dotnet add package IronBarCode
SHELL

Ces deux commandes téléchargent la version actuelle depuis NuGet et ajoutent la référence d'assembly à votre fichier projet. La bibliothèque cible .NET Standard 2.0 et fonctionne donc avec .NET Framework 4.6.2 à .NET 10 sans aucun module de compatibilité supplémentaire.

Après l'installation, définissez votre clé de licence avant d'appeler une méthode IronBarcode . Pour le développement et l'évaluation, une clé d'essai gratuite est disponible sur la page de licences IronBarcode :

IronBarCode.License.LicenseKey = "YOUR-LICENSE-KEY";
IronBarCode.License.LicenseKey = "YOUR-LICENSE-KEY";
Imports IronBarCode

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

Pour les déploiements conteneurisés, IronBarcode fonctionne avec Docker sous Linux , et pour les fonctions cloud, il prend en charge AWS Lambda et Azure Functions .

Comment valider les codes-barres scannés avec IronBarcode?

Quelle est la bonne approche pour la vérification du format ?

IronBarcode prend en charge plus de 30 symbologies de codes-barres, notamment Code 128 , EAN-13 , Code 39 , les codes QR et Data Matrix . Pour les applications de scanner USB, le modèle de validation réencode la chaîne scannée sous forme d'image de code-barres et la relit immédiatement via le décodeur. Cet aller-retour confirme que la chaîne est une valeur valide pour le format déclaré :

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

Pour les codes-barres GS1-128 utilisés dans les applications de la chaîne d'approvisionnement, la chaîne scannée comprend des préfixes d'identifiant d'application entre parenthèses tels que (01) pour le GTIN et (17) pour la date d'expiration. IronBarcode analyse automatiquement ces identifiants d'application lorsque vous spécifiez BarcodeEncoding.GS1_128.

Quelle logique de somme de contrôle EAN-13 les développeurs doivent-ils implémenter ?

Les applications de point de vente doivent souvent vérifier indépendamment les chiffres de contrôle EAN-13 avant de transmettre la valeur à une base de données de prix. La somme de contrôle de type Luhn pour EAN-13 alterne les valeurs 1 et 3 sur les 12 premiers chiffres :

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

Ce contrôle purement logique s'exécute avant l'encodage afin d'éviter la surcharge liée à la génération d'images aller-retour pour chaque scan dans un environnement de vente au détail à volume élevé. Selon la spécification GS1 , l'algorithme de chiffre de contrôle est identique pour UPC-A (12 chiffres) lorsque vous supprimez le zéro initial.

Comment générer des codes-barres de réponse à partir de données scannées ?

Quand une application doit-elle créer de nouveaux codes-barres après une numérisation ?

Un schéma courant dans la réception en entrepôt est le flux de travail " scan et réétiquetage " : un article entrant porte un code-barres fournisseur (souvent EAN-13 ou ITF-14), et le système de gestion d'entrepôt doit imprimer une étiquette interne Code 128 avec ses propres codes d'emplacement et de lot. Les fonctionnalités de génération d'IronBarcode gèrent cela en quelques lignes :

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

L'enregistrement au format PDF est particulièrement utile pour les imprimantes d'étiquettes qui acceptent les fichiers PDF en entrée via un partage réseau. Vous pouvez également exporter au format SVG pour obtenir des étiquettes thermiques vectorielles de haute qualité, ou exporter sous forme de flux d'octets pour l'envoyer directement à une API d'imprimante d'étiquettes.

IronBarcode prend en charge une personnalisation de style étendue, notamment les couleurs personnalisées, les ajustements de marge , les superpositions de texte lisibles par l'homme et, pour les codes QR, l'intégration de logos pour les étiquettes mobiles marquées de la marque.

Interface d'application Windows Forms démontrant les capacités de génération de codes-barres doubles d'IronBarcode. L'interface affiche la génération réussie d'un code-barres linéaire Code 128 et d'un code QR pour le numéro d'inventaire 'INV-20250917-helloworld'. Le champ de saisie situé en haut permet aux utilisateurs de saisir des codes d'inventaire personnalisés, avec un bouton  Générer  pour créer les codes-barres. Le message de succès  Article traité avec succès - Étiquettes générées  confirme que l'opération est terminée. Le code-barres Code 128 est désigné comme le format principal de suivi des stocks, tandis que le code QR ci-dessous est présenté comme une alternative adaptée aux appareils mobiles. L'application utilise un fond gris Professional avec une hiérarchie visuelle claire, démontrant comment IronBarcode permet aux développeurs de créer des systèmes de génération de codes-barres multiformats pour une gestion complète des stocks.

Comment créer une application de numérisation à haut volume complète ?

À quoi ressemble une implémentation de production basée sur une file d'attente ?

Pour les applications qui traitent des dizaines d'analyses par minute, un simple gestionnaire synchrone sur le thread d'interface utilisateur devient un goulot d'étranglement. Le modèle ci-dessous dissocie la capture du scan du traitement à l'aide d'un ConcurrentQueue<t> et d'une boucle de traitement en arrière-plan. L'API asynchrone d'IronBarcode gère la validation sans bloquer l'interface utilisateur :

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

Le SemaphoreSlim limite les tâches de validation simultanées au nombre de processeurs logiques, empêchant la création de threads incontrôlés lors d'événements de balayage en rafale. L'appel BeginInvoke renvoie les mises à jour de l'interface utilisateur au thread principal en toute sécurité.

Comment optimiser les performances pour différents volumes de numérisation ?

La propriété BarcodeReaderOptions.Speed accepte ReadingSpeed.Faster, ReadingSpeed.Balanced et ReadingSpeed.Detailed. Pour l'entrée du scanner USB où la valeur de la chaîne est déjà connue, Balanced est approprié -- le décodeur n'a besoin que de confirmer le format, pas de localiser un code-barres dans une image. Selon la documentation sur la vitesse de lecture d'IronBarcode , le mode Faster ignore certains algorithmes de correction de distorsion, ce qui est sûr pour une sortie de scanner propre mais peut manquer des codes-barres endommagés dans les scénarios basés sur l'image.

Le tableau suivant récapitule quand utiliser chaque mode de vitesse :

Modes de vitesse de lecture des IronBarcode et leurs cas d'utilisation appropriés
Mode vitesse Idéal pour Compromis
Plus rapide Entrée de scanner USB propre, débit élevé Peut ne pas détecter les codes-barres fortement endommagés ou déformés.
Équilibré Entrée mixte -- Scanner USB et importation d'images Utilisation modérée du processeur, bonne précision
Détaillé Étiquettes endommagées, impression à faible contraste, importations de PDF Utilisation du processeur plus élevée, débit le plus faible

Pour les applications qui traitent également des images ou des PDF en plus de l'entrée d'un scanner USB, IronBarcode peut lire les codes-barres des documents PDF et des fichiers TIFF multipages en utilisant la même interface API.

 Application Professional de lecture de codes-barres Windows Forms présentant les capacités de suivi d'inventaire en temps réel d'IronBarcode. L'interface présente un design épuré à deux panneaux avec un en-tête bleu foncé sophistiqué. Le panneau de gauche affiche une liste d'historique de numérisation montrant quatre articles d'inventaire numérisés avec succès (INV-001 à INV-004) avec des horodatages précis et des indicateurs d'état de numérisation. Chaque article comprend des métadonnées détaillées telles que le type de code-barres et le niveau de confiance. Le panneau de droite affiche un code-barres récapitulatif généré dynamiquement indiquant  Articles : 4  avec un style Professional et des marges appropriées. Les boutons d'action situés en bas comprennent  Effacer la liste ,  Exporter les données  et  Imprimer les étiquettes  pour une gestion complète des stocks. La barre d'état indique  Scanner : Connecté  | Mode : Continu | Dernière analyse : il y a 2 secondes , démontrant les capacités de surveillance en temps réel de l'application et sa conception Professional adaptée aux entreprises, IronBarcode permet pour les systèmes d'inventaire de production.

Comment gérez-vous les cas particuliers et les erreurs dans les applications de numérisation ?

Quels modes de défaillance les développeurs doivent-ils anticiper ?

Les applications de numérisation USB présentent des défaillances prévisibles. Les problèmes les plus courants et leurs solutions sont les suivants :

Déconnexion du scanner -- Lorsqu'un scanner USB est débranché, le contrôle TextBox perd son clavier virtuel. La solution la plus simple consiste en une minuterie périodique qui vérifie _inputBox.Focused et la recentre si le scanner est toujours répertorié dans les périphériques HID connectés. Pour les scanners série, SerialPort.GetPortNames() détecte la reconnexion.

Formats de codes-barres ambigus -- Certains produits portent des codes-barres valides dans plusieurs symbologies. Par exemple, une chaîne de 12 chiffres est un code UPC-A valide et également un code 128 valide. Spécifier ExpectBarcodeTypes dans BarcodeReaderOptions contraint le décodeur à vos formats attendus et élimine toute ambiguïté. Le guide de dépannage IronBarcode contient des conseils de reconnaissance spécifiques au format.

Exceptions de format non valide -- Si BarcodeWriter.CreateBarcode reçoit une chaîne qui enfreint les règles d'encodage sélectionnées (par exemple, des caractères alphabétiques dans un champ EAN-13 uniquement numérique), il génère une exception IronBarCode.Exceptions.InvalidBarcodeException. Envelopper l'appel dans un bloc try-catch et recourir à une validation basée uniquement sur les chaînes de caractères permet à l'application de continuer à fonctionner.

Collisions de synchronisation des frappes au clavier -- Dans les environnements où les opérateurs saisissent également manuellement du texte dans la même zone de texte, l'approche du minuteur en rafale décrite précédemment constitue la principale défense. Une protection secondaire concerne la longueur minimale : la plupart des codes-barres réels comportent au moins 8 caractères, donc les chaînes plus courtes peuvent être traitées comme une entrée clavier.

La documentation Microsoft .NET sur System.IO.Ports.SerialPort est utile pour le dépannage de la connectivité du scanner série, en particulier autour des paramètres ReadTimeout et WriteTimeout. Pour assurer la conformité réglementaire dans le secteur du commerce de détail, les spécifications générales GS1 définissent des plages de valeurs valides pour chaque identifiant d'application.

Comment étendre l'application aux plateformes mobiles et web ?

Le modèle d'interface du scanner illustré ci-dessus -- IScannerInput avec BarcodeScanned événement -- abstrait le matériel de la logique de traitement. L'échange d'implémentations permet d'exécuter le même code de validation et de génération sur différentes plateformes :

  • .NET MAUI fournit une implémentation de scanner basée sur une caméra pour les tablettes Android et iOS utilisées comme stations de réception mobiles
  • Blazor Server prend en charge la numérisation basée sur le navigateur avec un accès à la caméra JavaScript alimentant le même événement BarcodeScanned
  • Les implémentations natives Android et iOS offrent aux développeurs mobiles la possibilité de scanner avec l'appareil photo grâce au même décodeur IronBarcode côté serveur.

Pour les architectures natives du cloud, les étapes de validation et de génération d'étiquettes peuvent être exécutées en tant que fonctions Azure déclenchées par des messages de file d'attente, l'application de bureau servant uniquement de passerelle d'entrée du scanner. Cette séparation est particulièrement utile lorsque la logique d'impression des étiquettes doit être centralisée à des fins d'audit de conformité.

Quelles sont vos prochaines étapes ?

La création d'une application de lecteur de codes-barres USB avec IronBarcode comprend quatre étapes concrètes : la capture de l'entrée du clavier avec détection de synchronisation en rafale, la validation de la valeur scannée par le décodeur d'IronBarcode, la génération d'étiquettes de réponse au format requis et le traitement de volumes de numérisation élevés avec une file d'attente concurrente. Chaque étape est indépendante et peut être testée isolément.

À partir de là, envisagez d'étendre l'application avec la lecture de plusieurs codes-barres pour les scénarios de traitement par lots, l'optimisation de la zone de recadrage pour les entrées basées sur l'image ou la prise en charge des codes-barres MSI pour les équipements d'entrepôt plus anciens. La documentation IronBarcode couvre tous les formats pris en charge et les options de configuration avancées du lecteur.

Démarrez un essai gratuit pour obtenir une clé de licence de développement et commencez dès aujourd'hui à intégrer IronBarcode à votre application de numérisation.

Questions Fréquemment Posées

Qu'est-ce qu'IronBarcode et comment est-il lié aux scanners de codes-barres USB ?

IronBarcode est une bibliothèque qui permet aux développeurs de créer des applications C# robustes pour le scan de codes-barres USB. Elle offre des fonctionnalités telles que la validation de codes-barres, l'extraction de données et la génération de codes-barres.

IronBarcode peut-il valider les données de codes-barres d'un scanner USB ?

Oui, IronBarcode peut valider les données de codes-barres capturées par un scanner USB, garantissant l'intégrité et la précision des données dans vos applications C#.

Comment IronBarcode gère-t-il la génération de codes-barres ?

IronBarcode peut générer de nouveaux codes-barres à la volée, permettant aux développeurs de créer et d'imprimer des codes-barres facilement au sein de leurs applications C#.

Y a-t-il un support de gestion des erreurs dans IronBarcode pour le scan de codes-barres USB ?

Oui, IronBarcode inclut une gestion complète des erreurs pour gérer les problèmes courants qui peuvent survenir durant le scan et le traitement des codes-barres USB.

Quels types de codes-barres peuvent être numérisés avec IronBarcode ?

IronBarcode prend en charge le scan d'une large gamme de symbologies de codes-barres, incluant les QR codes, UPC, Code 39 et plus, le rendant polyvalent pour diverses applications.

IronBarcode peut-il extraire des informations structurées à partir de codes-barres scannés ?

Oui, IronBarcode peut extraire des informations structurées à partir de codes-barres scannés, aidant ainsi le traitement et la gestion efficaces des données.

Comment puis-je commencer à construire une application de scanner de codes-barres USB en C# ?

Pour commencer à construire une application de scanner de codes-barres USB en C#, vous pouvez utiliser IronBarcode ainsi que les exemples de code fournis et la documentation pour guider votre processus de développement.

Jordi Bardia
Ingénieur logiciel
Jordi est le plus compétent en Python, C# et C++, et lorsqu'il ne met pas à profit ses compétences chez Iron Software, il programme des jeux. Partageant les responsabilités des tests de produit, du développement de produit et de la recherche, Jordi apporte une immense valeur à l'amé...
Lire la suite

Équipe de soutien Iron

Nous sommes en ligne 24 heures sur 24, 5 jours sur 7.
Chat
Email
Appelez-moi