フッターコンテンツにスキップ
IRONBARCODEの使用

C# USBバーコードスキャナ:完全なスキャンアプリケーションを構築する

USBバーコードスキャナーは、標準的なキーボード入力デバイスとしてC#アプリケーションに接続され、スキャンしたデータを入力文字として送信し、その後にEnterキーを押します。 このHIDキーボードウェッジの動作により、統合が容易になります。特別なドライバやSDKを必要とせずに、アプリケーションはテキスト入力を受け取ることができます。 IronBarcodeは、入力された生データを処理してフォーマットを検証し、構造化データを抽出し、応答バーコードを生成することで、単純なスキャンイベントを在庫管理、小売店のPOSシステム、物流追跡システムのための完全なデータパイプラインへと変換します。

小売業、倉庫業、製造業のいずれにおいても、正確かつ迅速なバーコードスキャンが不可欠である。 開発者がUSBスキャナをWindows FormsまたはWPFアプリケーションに接続すると、スキャナはキーボードとまったく同じように動作します。データはテキストボックスに表示され、Enterキーを押すとバーコード全体が受信されたことが示されます。 課題はデータの収集ではない。 正しく処理されています。 IronBarcodeのバーコード検証機能は、フォーマットの整合性をチェックし、バッチ番号やGS1アプリケーション識別子などのフィールドを抽出し、それに応じて新しいバーコードを即座に生成できます。

このガイドでは、実運用に対応したC# USBバーコードスキャナアプリケーションを段階的に構築する方法を解説します。ライブラリのインストール、スキャナ入力の取得、バーコード形式の検証、応答ラベルの生成、そして大容量キューベースプロセッサの組み立てを行います。 各セクションには、 .NET 10 を対象とした、実行可能な完全なコードが含まれており、必要に応じてトップレベルのステートメントスタイルが使用されています。

USBバーコードスキャナーはC#でどのように動作するのですか?

HIDキーボードのウェッジモードが統合を簡素化する理由とは?

ほとんどのUSBバーコードスキャナーは、デフォルトでHIDキーボードウェッジモードに設定されて出荷されます。 Windowsマシンに接続すると、オペレーティングシステムはそれをUSBストレージデバイス(設定用)とキーボード(データ入力用)の両方として認識します。 バーコードがスキャンされると、デバイスはデコードされたバーコード値をキーストロークに変換し、フォーカスのあるアプリケーションウィンドウに送信し、最後に改行を追加します。

C#開発者の視点から見ると、これはベンダーのSDK、COMライブラリ、特別なUSB APIが不要であることを意味します。入力を取得するには、KeyDownハンドラーを備えた標準的なTextBoxだけで十分です。 主な統合上の課題は、スキャナー入力と実際のキーボード入力を区別することである。 スキャナーは通常、非常に短い時間(多くの場合50ミリ秒未満)で全ての文字を出力するのに対し、人間のタイピングではキーストロークが数百ミリ秒にわたって分散される。 連打のタイミングを計ることは、意図しないキー入力を排除する確実な方法です。

プロ仕様のスキャナは、シリアル(RS-232または仮想COMポート)モードと直接USB HIDモードもサポートしており、接頭辞/接尾辞文字やスキャントリガーをより細かく制御できます。 以下のインターフェースパターンは、両方のケースに対応します。

public interface IScannerInput
{
    event EventHandler<string> BarcodeScanned;
    void StartListening();
    void StopListening();
}

public class KeyboardWedgeScanner : IScannerInput
{
    public event EventHandler<string> BarcodeScanned;
    private readonly TextBox _inputBox;
    private readonly System.Windows.Forms.Timer _burstTimer;
    private readonly System.Text.StringBuilder _buffer = new();

    public KeyboardWedgeScanner(TextBox inputBox)
    {
        _inputBox = inputBox;
        _burstTimer = new System.Windows.Forms.Timer { Interval = 80 };
        _burstTimer.Tick += OnBurstTimeout;
        _inputBox.KeyPress += OnKeyPress;
    }

    private void OnKeyPress(object sender, KeyPressEventArgs e)
    {
        if (e.KeyChar == (char)Keys.Enter)
        {
            _burstTimer.Stop();
            string value = _buffer.ToString().Trim();
            _buffer.Clear();
            if (value.Length > 0)
                BarcodeScanned?.Invoke(this, value);
        }
        else
        {
            _buffer.Append(e.KeyChar);
            _burstTimer.Stop();
            _burstTimer.Start();
        }
        e.Handled = true;
    }

    private void OnBurstTimeout(object sender, EventArgs e)
    {
        _burstTimer.Stop();
        _buffer.Clear(); // incomplete burst -- discard
    }

    public void StartListening() => _inputBox.Focus();
    public void StopListening() => _inputBox.Enabled = false;
}

public class SerialPortScanner : IScannerInput
{
    public event EventHandler<string> BarcodeScanned;
    private readonly System.IO.Ports.SerialPort _port;

    public SerialPortScanner(string portName, int baudRate = 9600)
    {
        _port = new System.IO.Ports.SerialPort(portName, baudRate);
        _port.DataReceived += OnDataReceived;
    }

    private void OnDataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
    {
        string data = _port.ReadLine().Trim();
        if (data.Length > 0)
            BarcodeScanned?.Invoke(this, data);
    }

    public void StartListening() => _port.Open();
    public void StopListening() => _port.Close();
}
public interface IScannerInput
{
    event EventHandler<string> BarcodeScanned;
    void StartListening();
    void StopListening();
}

public class KeyboardWedgeScanner : IScannerInput
{
    public event EventHandler<string> BarcodeScanned;
    private readonly TextBox _inputBox;
    private readonly System.Windows.Forms.Timer _burstTimer;
    private readonly System.Text.StringBuilder _buffer = new();

    public KeyboardWedgeScanner(TextBox inputBox)
    {
        _inputBox = inputBox;
        _burstTimer = new System.Windows.Forms.Timer { Interval = 80 };
        _burstTimer.Tick += OnBurstTimeout;
        _inputBox.KeyPress += OnKeyPress;
    }

    private void OnKeyPress(object sender, KeyPressEventArgs e)
    {
        if (e.KeyChar == (char)Keys.Enter)
        {
            _burstTimer.Stop();
            string value = _buffer.ToString().Trim();
            _buffer.Clear();
            if (value.Length > 0)
                BarcodeScanned?.Invoke(this, value);
        }
        else
        {
            _buffer.Append(e.KeyChar);
            _burstTimer.Stop();
            _burstTimer.Start();
        }
        e.Handled = true;
    }

    private void OnBurstTimeout(object sender, EventArgs e)
    {
        _burstTimer.Stop();
        _buffer.Clear(); // incomplete burst -- discard
    }

    public void StartListening() => _inputBox.Focus();
    public void StopListening() => _inputBox.Enabled = false;
}

public class SerialPortScanner : IScannerInput
{
    public event EventHandler<string> BarcodeScanned;
    private readonly System.IO.Ports.SerialPort _port;

    public SerialPortScanner(string portName, int baudRate = 9600)
    {
        _port = new System.IO.Ports.SerialPort(portName, baudRate);
        _port.DataReceived += OnDataReceived;
    }

    private void OnDataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
    {
        string data = _port.ReadLine().Trim();
        if (data.Length > 0)
            BarcodeScanned?.Invoke(this, data);
    }

    public void StartListening() => _port.Open();
    public void StopListening() => _port.Close();
}
Imports System
Imports System.Text
Imports System.Windows.Forms
Imports System.IO.Ports

Public Interface IScannerInput
    Event BarcodeScanned As EventHandler(Of String)
    Sub StartListening()
    Sub StopListening()
End Interface

Public Class KeyboardWedgeScanner
    Implements IScannerInput

    Public Event BarcodeScanned As EventHandler(Of String) Implements IScannerInput.BarcodeScanned
    Private ReadOnly _inputBox As TextBox
    Private ReadOnly _burstTimer As Timer
    Private ReadOnly _buffer As New StringBuilder()

    Public Sub New(inputBox As TextBox)
        _inputBox = inputBox
        _burstTimer = New Timer With {.Interval = 80}
        AddHandler _burstTimer.Tick, AddressOf OnBurstTimeout
        AddHandler _inputBox.KeyPress, AddressOf OnKeyPress
    End Sub

    Private Sub OnKeyPress(sender As Object, e As KeyPressEventArgs)
        If e.KeyChar = ChrW(Keys.Enter) Then
            _burstTimer.Stop()
            Dim value As String = _buffer.ToString().Trim()
            _buffer.Clear()
            If value.Length > 0 Then
                RaiseEvent BarcodeScanned(Me, value)
            End If
        Else
            _buffer.Append(e.KeyChar)
            _burstTimer.Stop()
            _burstTimer.Start()
        End If
        e.Handled = True
    End Sub

    Private Sub OnBurstTimeout(sender As Object, e As EventArgs)
        _burstTimer.Stop()
        _buffer.Clear() ' incomplete burst -- discard
    End Sub

    Public Sub StartListening() Implements IScannerInput.StartListening
        _inputBox.Focus()
    End Sub

    Public Sub StopListening() Implements IScannerInput.StopListening
        _inputBox.Enabled = False
    End Sub
End Class

Public Class SerialPortScanner
    Implements IScannerInput

    Public Event BarcodeScanned As EventHandler(Of String) Implements IScannerInput.BarcodeScanned
    Private ReadOnly _port As SerialPort

    Public Sub New(portName As String, Optional baudRate As Integer = 9600)
        _port = New SerialPort(portName, baudRate)
        AddHandler _port.DataReceived, AddressOf OnDataReceived
    End Sub

    Private Sub OnDataReceived(sender As Object, e As SerialDataReceivedEventArgs)
        Dim data As String = _port.ReadLine().Trim()
        If data.Length > 0 Then
            RaiseEvent BarcodeScanned(Me, data)
        End If
    End Sub

    Public Sub StartListening() Implements IScannerInput.StartListening
        _port.Open()
    End Sub

    Public Sub StopListening() Implements IScannerInput.StopListening
        _port.Close()
    End Sub
End Class
$vbLabelText   $csharpLabel

キーボードウェッジ実装におけるバーストタイマーが重要なポイントです。 キー入力のたびにリセットされ、文字の入力が停止した場合にのみ作動します。つまり、キーボード入力が遅い通常のユーザーの場合、入力が不完全な場合はバーコードスキャンとして扱われるのではなく、入力内容が破棄されます。

複数のスキャナーブランドをどのように扱っていますか?

Enterprise環境では、ハネウェル、ゼブラ(旧シンボル/モトローラ)、データロジックのスキャナが同じフロアで混在して使用されていることがよくある。 各ベンダーは、独自のデフォルトの終端文字、ボーレート、および接頭辞/接尾辞の規則を持っています。 構成モデルによって、アプリケーションの柔軟性が維持されます。

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

これらの設定を構成ファイルまたはデータベースに保存することで、倉庫スタッフは再配置を必要とせずにスキャナーのモデルを交換できるようになります。 ScannerType フィールドは、起動時にどの IScannerInput 実装がインスタンス化されるかを決定します。

C#プロジェクトにIronBarcodeをインストールするにはどうすればよいですか?

NuGetを介してIronBarcodeを追加する最速の方法は何ですか?

Visual StudioでPackage Manager Consoleを開き、実行してください:

Install-Package IronBarCode
Install-Package IronBarCode
SHELL

または、 .NET CLI を使用してください。

dotnet add package IronBarCode
dotnet add package IronBarCode
SHELL

どちらのコマンドも、 NuGetから最新リリースを取得し、アセンブリ参照をプロジェクトファイルに追加します。このライブラリは.NET Standard 2.0 を対象としているため、追加の互換性シムなしで.NET Framework 4.6.2 から.NET 10 まで動作します。

インストール後、 IronBarcodeのメソッドを呼び出す前にライセンスキーを設定してください。 開発および評価目的では、IronBarcodeのライセンスページから無料トライアルキーを入手できます。

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

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

コンテナ化されたデプロイメントの場合、 IronBarcodeはLinux上のDockerと連携し、クラウド関数に関してはAWS LambdaAzure Functionsをサポートしています。

IronBarcodeでスキャンしたバーコードを検証するにはどうすればよいですか?

フォーマット検証における最適なアプローチとは?

IronBarcodeは、Code 128EAN-13Code 39QRコードデータマトリックスなど、 IronBarcode種類以上のバーコードシンボルをサポートしています。 USBスキャナアプリケーションの場合、検証パターンはスキャンされた文字列をバーコード画像として再エンコードし、デコーダを通して直ちに読み戻します。 この往復通信により、文字列が宣言されたフォーマットに対して有効な値であることが確認されます。

public class BarcodeValidator
{
    public async Task<ValidationResult> ValidateAsync(string scannedText, BarcodeEncoding preferredFormat = BarcodeEncoding.Code128)
    {
        var result = new ValidationResult { RawInput = scannedText };

        try
        {
            var barcode = BarcodeWriter.CreateBarcode(scannedText, preferredFormat);
            var readResults = await BarcodeReader.ReadAsync(barcode.ToBitmap());

            if (readResults.Any())
            {
                var first = readResults.First();
                result.IsValid = true;
                result.Format = first.BarcodeType;
                result.Value = first.Value;
                result.Confidence = first.Confidence;
            }
            else
            {
                result.IsValid = false;
                result.Error = "No barcode could be decoded from the scanned input.";
            }
        }
        catch (Exception ex)
        {
            result.IsValid = false;
            result.Error = ex.Message;
        }

        return result;
    }
}

public record ValidationResult
{
    public string RawInput { get; init; } = "";
    public bool IsValid { get; set; }
    public BarcodeEncoding Format { get; set; }
    public string Value { get; set; } = "";
    public float Confidence { get; set; }
    public string Error { get; set; } = "";
}
public class BarcodeValidator
{
    public async Task<ValidationResult> ValidateAsync(string scannedText, BarcodeEncoding preferredFormat = BarcodeEncoding.Code128)
    {
        var result = new ValidationResult { RawInput = scannedText };

        try
        {
            var barcode = BarcodeWriter.CreateBarcode(scannedText, preferredFormat);
            var readResults = await BarcodeReader.ReadAsync(barcode.ToBitmap());

            if (readResults.Any())
            {
                var first = readResults.First();
                result.IsValid = true;
                result.Format = first.BarcodeType;
                result.Value = first.Value;
                result.Confidence = first.Confidence;
            }
            else
            {
                result.IsValid = false;
                result.Error = "No barcode could be decoded from the scanned input.";
            }
        }
        catch (Exception ex)
        {
            result.IsValid = false;
            result.Error = ex.Message;
        }

        return result;
    }
}

public record ValidationResult
{
    public string RawInput { get; init; } = "";
    public bool IsValid { get; set; }
    public BarcodeEncoding Format { get; set; }
    public string Value { get; set; } = "";
    public float Confidence { get; set; }
    public string Error { get; set; } = "";
}
Imports System
Imports System.Linq
Imports System.Threading.Tasks

Public Class BarcodeValidator
    Public Async Function ValidateAsync(scannedText As String, Optional preferredFormat As BarcodeEncoding = BarcodeEncoding.Code128) As Task(Of ValidationResult)
        Dim result As New ValidationResult With {.RawInput = scannedText}

        Try
            Dim barcode = BarcodeWriter.CreateBarcode(scannedText, preferredFormat)
            Dim readResults = Await BarcodeReader.ReadAsync(barcode.ToBitmap())

            If readResults.Any() Then
                Dim first = readResults.First()
                result.IsValid = True
                result.Format = first.BarcodeType
                result.Value = first.Value
                result.Confidence = first.Confidence
            Else
                result.IsValid = False
                result.Error = "No barcode could be decoded from the scanned input."
            End If
        Catch ex As Exception
            result.IsValid = False
            result.Error = ex.Message
        End Try

        Return result
    End Function
End Class

Public Class ValidationResult
    Public Property RawInput As String = ""
    Public Property IsValid As Boolean
    Public Property Format As BarcodeEncoding
    Public Property Value As String = ""
    Public Property Confidence As Single
    Public Property Error As String = ""
End Class
$vbLabelText   $csharpLabel

サプライチェーンアプリケーションで使用されるGS1-128バーコードの場合、スキャンされた文字列には、GTINの場合は(17)のように、括弧内にアプリケーション識別子の接頭辞が含まれます。 IronBarcode は、BarcodeEncoding.GS1_128 を指定すると、これらのアプリケーション識別子を自動的に解析します。

開発者はどのEAN-13チェックサムロジックを実装すべきか?

小売店のPOSアプリケーションでは、価格検索に値を渡す前に、EAN-13チェックデジットを個別に検証する必要がある場合がよくあります。EAN-13のLuhnアルゴリズムによるチェックサムは、最初の12桁で1と3の重みが交互に現れます。

public static bool ValidateEan13Checksum(string value)
{
    if (value.Length != 13 || !value.All(char.IsDigit))
        return false;

    int sum = 0;
    for (int i = 0; i < 12; i++)
    {
        int digit = value[i] - '0';
        sum += (i % 2 == 0) ? digit : digit * 3;
    }

    int expectedCheck = (10 - (sum % 10)) % 10;
    return expectedCheck == (value[12] - '0');
}
public static bool ValidateEan13Checksum(string value)
{
    if (value.Length != 13 || !value.All(char.IsDigit))
        return false;

    int sum = 0;
    for (int i = 0; i < 12; i++)
    {
        int digit = value[i] - '0';
        sum += (i % 2 == 0) ? digit : digit * 3;
    }

    int expectedCheck = (10 - (sum % 10)) % 10;
    return expectedCheck == (value[12] - '0');
}
Public Shared Function ValidateEan13Checksum(value As String) As Boolean
    If value.Length <> 13 OrElse Not value.All(AddressOf Char.IsDigit) Then
        Return False
    End If

    Dim sum As Integer = 0
    For i As Integer = 0 To 11
        Dim digit As Integer = AscW(value(i)) - AscW("0"c)
        sum += If(i Mod 2 = 0, digit, digit * 3)
    Next

    Dim expectedCheck As Integer = (10 - (sum Mod 10)) Mod 10
    Return expectedCheck = (AscW(value(12)) - AscW("0"c))
End Function
$vbLabelText   $csharpLabel

この純粋な論理チェックはエンコード前に実行され、大量のデータを扱う小売環境において、スキャンごとに往復画像生成を行うオーバーヘッドを回避する。 GS1の仕様によると、先頭のゼロを削除した場合、UPC-A(12桁)のチェックデジットアルゴリズムは同じです。

スキャンした入力データから応答バーコードを生成するにはどうすればよいですか?

アプリケーションはスキャン後にいつ新しいバーコードを作成すべきでしょうか?

倉庫での受入業務でよく見られるのは、"スキャンしてラベルを貼り直す"というワークフローです。入荷品には仕入先のバーコード(多くの場合EAN-13またはITF-14)が付いており、倉庫管理システムは、独自のロケーションコードとロットコードが記載された内部コード128ラベルを印刷する必要があります。 IronBarcodeの生成機能を使えば、これを数行で処理できます。

public class InventoryLabelGenerator
{
    private readonly string _outputDirectory;

    public InventoryLabelGenerator(string outputDirectory)
    {
        _outputDirectory = outputDirectory;
        Directory.CreateDirectory(_outputDirectory);
    }

    public async Task<string> GenerateLabelAsync(string internalCode, string locationCode)
    {
        string fullCode = $"{internalCode}|{locationCode}|{DateTime.UtcNow:yyyyMMdd}";

        // Primary Code 128 label for scanners
        var linearBarcode = BarcodeWriter.CreateBarcode(fullCode, BarcodeEncoding.Code128);
        linearBarcode.ResizeTo(500, 140);
        linearBarcode.SetMargins(12);
        linearBarcode.AddAnnotationTextAboveBarcode(fullCode);
        linearBarcode.ChangeBarCodeColor(IronSoftware.Drawing.Color.Black);

        // QR code companion for mobile apps
        var qrCode = BarcodeWriter.CreateQrCode(fullCode);
        qrCode.ResizeTo(200, 200);
        qrCode.SetMargins(8);

        string timestamp = DateTime.UtcNow.ToString("yyyyMMddHHmmss");
        string pngPath = Path.Combine(_outputDirectory, $"{internalCode}_{timestamp}.png");
        string pdfPath = Path.Combine(_outputDirectory, $"{internalCode}_{timestamp}.pdf");

        await Task.Run(() =>
        {
            linearBarcode.SaveAsPng(pngPath);
            linearBarcode.SaveAsPdf(pdfPath);
        });

        return pngPath;
    }
}
public class InventoryLabelGenerator
{
    private readonly string _outputDirectory;

    public InventoryLabelGenerator(string outputDirectory)
    {
        _outputDirectory = outputDirectory;
        Directory.CreateDirectory(_outputDirectory);
    }

    public async Task<string> GenerateLabelAsync(string internalCode, string locationCode)
    {
        string fullCode = $"{internalCode}|{locationCode}|{DateTime.UtcNow:yyyyMMdd}";

        // Primary Code 128 label for scanners
        var linearBarcode = BarcodeWriter.CreateBarcode(fullCode, BarcodeEncoding.Code128);
        linearBarcode.ResizeTo(500, 140);
        linearBarcode.SetMargins(12);
        linearBarcode.AddAnnotationTextAboveBarcode(fullCode);
        linearBarcode.ChangeBarCodeColor(IronSoftware.Drawing.Color.Black);

        // QR code companion for mobile apps
        var qrCode = BarcodeWriter.CreateQrCode(fullCode);
        qrCode.ResizeTo(200, 200);
        qrCode.SetMargins(8);

        string timestamp = DateTime.UtcNow.ToString("yyyyMMddHHmmss");
        string pngPath = Path.Combine(_outputDirectory, $"{internalCode}_{timestamp}.png");
        string pdfPath = Path.Combine(_outputDirectory, $"{internalCode}_{timestamp}.pdf");

        await Task.Run(() =>
        {
            linearBarcode.SaveAsPng(pngPath);
            linearBarcode.SaveAsPdf(pdfPath);
        });

        return pngPath;
    }
}
Imports System.IO
Imports System.Threading.Tasks

Public Class InventoryLabelGenerator
    Private ReadOnly _outputDirectory As String

    Public Sub New(outputDirectory As String)
        _outputDirectory = outputDirectory
        Directory.CreateDirectory(_outputDirectory)
    End Sub

    Public Async Function GenerateLabelAsync(internalCode As String, locationCode As String) As Task(Of String)
        Dim fullCode As String = $"{internalCode}|{locationCode}|{DateTime.UtcNow:yyyyMMdd}"

        ' Primary Code 128 label for scanners
        Dim linearBarcode = BarcodeWriter.CreateBarcode(fullCode, BarcodeEncoding.Code128)
        linearBarcode.ResizeTo(500, 140)
        linearBarcode.SetMargins(12)
        linearBarcode.AddAnnotationTextAboveBarcode(fullCode)
        linearBarcode.ChangeBarCodeColor(IronSoftware.Drawing.Color.Black)

        ' QR code companion for mobile apps
        Dim qrCode = BarcodeWriter.CreateQrCode(fullCode)
        qrCode.ResizeTo(200, 200)
        qrCode.SetMargins(8)

        Dim timestamp As String = DateTime.UtcNow.ToString("yyyyMMddHHmmss")
        Dim pngPath As String = Path.Combine(_outputDirectory, $"{internalCode}_{timestamp}.png")
        Dim pdfPath As String = Path.Combine(_outputDirectory, $"{internalCode}_{timestamp}.pdf")

        Await Task.Run(Sub()
                           linearBarcode.SaveAsPng(pngPath)
                           linearBarcode.SaveAsPdf(pdfPath)
                       End Sub)

        Return pngPath
    End Function
End Class
$vbLabelText   $csharpLabel

PDF形式で保存する機能は、ネットワーク共有経由でPDF入力を受け付けるラベルプリンターにとって特に便利です。 ベクター品質のサーマルラベル出力用にSVG形式でエクスポートすることも、ラベルプリンターAPIに直接送信するためにバイトストリームとしてエクスポートすることもできます。

IronBarcodeは、カスタムカラー、余白調整、人間が読みやすいテキストオーバーレイ、QRコードの場合はブランドロゴ入りのモバイルラベルへのロゴ埋め込みなど、幅広いスタイルカスタマイズをサポートしています。

IronBarcodeのデュアルバーコード生成機能をデモンストレーションするWindows Formsアプリケーションインターフェイス。 インターフェースには、在庫番号INV-20250917-helloworldに対して、Code 128リニアバーコードとQRコードの両方が正常に生成されたことが示されています。 上部の入力欄では、ユーザーが独自の在庫コードを入力でき、生成ボタンをクリックするとバーコードが生成されます。 アイテムの処理に成功しました - ラベルが生成されましたという成功メッセージが表示されれば、操作が完了したことがわかります。 Code 128 バーコードは主要な在庫追跡フォーマットとしてラベル付けされており、その下の QR コードはモバイルフレンドリーな代替フォーマットとしてマークされています。アプリケーションは、明確な視覚的階層を備えたProfessionalグレーの背景を使用しており、 IronBarcode が開発者に完全な在庫管理のためのマルチフォーマット バーコード生成システムの作成をどのように可能にするかを示しています。

大量のデータをスキャンする完全なアプリケーションを構築するにはどうすればよいでしょうか?

本番環境におけるキューベースの実装はどのようなものですか?

1分間に数十回のスキャンを処理するアプリケーションでは、UIスレッド上の単純な同期ハンドラがボトルネックとなる。 以下のパターンでは、ConcurrentQueue<t> とバックグラウンド処理ループを使用して、スキャンキャプチャと処理を分離しています。IronBarcodeの非同期 API は、 UI をブロックすることなく検証を処理します。

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

SemaphoreSlim は、同時検証タスクを論理プロセッサの数に制限し、バーストスキャンイベント中にスレッドが暴走するのを防ぎます。 BeginInvoke は、UI の更新を安全にメインスレッドにマーシャリングします。

スキャン量に応じてパフォーマンスを調整するにはどうすれば良いですか?

BarcodeReaderOptions.Speed プロパティは、ReadingSpeed.Balanced、および ReadingSpeed.Detailed を受け入れます。 文字列値が既知であるUSBスキャナ入力の場合、Balanced が適切です。デコーダはフォーマットを確認するだけでよく、画像内のバーコードを探す必要はありません。 IronBarcodeの読み取り速度に関するドキュメントによると、Faster モードでは歪み補正アルゴリズムの一部がスキップされるため、スキャナーの出力がきれいな場合は安全ですが、画像ベースのシナリオでは破損したバーコードが見落とされる可能性があります。

各速度モードをいつ使用すべきかをまとめた表を以下に示します。

IronBarcodeの読み取り速度モードとその適切な使用例
スピードモード 最適な用途 トレード・オフ
Faster クリーンなUSBスキャナー入力、大容量スループット ひどく損傷している、または歪んでいるバーコードは見逃す可能性があります。
Balanced 入力形式が混在している -- USBスキャナーと画像インポート CPU使用率は中程度、精度も良好。
Detailed ラベルの破損、低コントラスト印刷、PDFインポート CPU使用率が高く、スループットが最も遅い

USBスキャナーからの入力に加えて画像やPDFも処理するアプリケーションの場合、 IronBarcodeは同じAPIインターフェースを使用してPDFドキュメント複数ページのTIFFファイルからバーコードを読み取ることができます。

 IronBarcode のリアルタイム在庫追跡機能を紹介するProfessionalWindows Forms バーコード スキャナー アプリケーション。 インターフェースは、洗練されたダークブルーのヘッダーを備えた、すっきりとした2パネルデザインを採用しています。 左側のパネルには、スキャン履歴リストが表示され、スキャンに成功した4つの在庫品目(INV-001~INV-004)が、正確なタイムスタンプとスキャンステータス表示とともに表示されます。 各アイテムには、バーコードの種類や信頼度レベルなどの詳細なメタデータが含まれています。 右側のパネルには、Professionalスタイルと適切な余白でアイテム数:4と表示された、動的に生成された要約バーコードが表示されます。 下部にある操作ボタンには、リストのクリア、データのエクスポート、ラベルの印刷などがあり、在庫管理を包括的に行うことができます。 ステータスバーにはスキャナー:接続済みと表示されます。 | モード: 連続 | 最終スキャン: 2秒前と表示され、 IronBarcodeが生産在庫管理システム向けに実現する、アプリケーションのリアルタイム監視機能とProfessionalなエンタープライズ対応設計を示しています。

スキャナアプリケーションにおけるエッジケースやエラーはどのように処理しますか?

開発者はどのような障害モードを想定しておくべきか?

USBスキャナーアプリケーションは、予測可能な形で不具合を起こす。 最も一般的な問題点とその対策は以下のとおりです。

スキャナの接続解除-- USBスキャナが取り外されると、キーボードウェッジのTextBoxは仮想キーボードを失います。 最も簡単な対策は、_inputBox.Focused を定期的にチェックし、スキャナが接続されている HID デバイスにまだリストされている場合は、それを再フォーカスするタイマーを使用することです。 シリアルスキャナの場合、SerialPort.GetPortNames() は再接続を検出します。

曖昧なバーコード形式― 一部の製品には、複数のシンボル体系で有効なバーコードが付いています。 例えば、12桁の文字列は有効なUPC-Aであると同時に有効なCode 128でもあります。BarcodeReaderOptionsExpectBarcodeTypes を指定することで、デコーダーを想定されるフォーマットに制約し、曖昧さを排除できます。 IronBarcodeのトラブルシューティングガイドには、フォーマット固有の認識に関するヒントが記載されています。

無効なフォーマット例外-- BarcodeWriter.CreateBarcode が選択されたエンコーディングのルールに違反する文字列 (たとえば、数値のみの EAN-13 フィールド内の英字) を受け取った場合、IronBarCode.Exceptions.InvalidBarcodeException がスローされます。 呼び出しをtry-catchブロックで囲み、文字列のみの検証パスにフォールバックすることで、アプリケーションの実行を維持できます。

キーストロークのタイミング衝突-- オペレーターが同じテキストボックスに手動で入力する環境では、前述のバーストタイマー方式が主な防御策となります。 二次的なガードとして最小文字数が定められています。実際のバーコードのほとんどは少なくとも8文字なので、それより短い文字列はキーボード入力として扱われます。

Microsoft .NETのSystem.IO.Ports.SerialPortに関するドキュメントは、シリアルスキャナの接続に関するトラブルシューティング、特に ReadTimeout および WriteTimeout 設定に関するトラブルシューティングに役立ちます。 小売業における規制遵守のため、 GS1一般仕様では、各アプリケーション識別子の有効な値の範囲が定義されています。

アプリケーションをモバイルプラットフォームやウェブプラットフォームに拡張するにはどうすればよいですか?

上記のスキャナインターフェイスパターン(BarcodeScanned イベント)は、ハードウェアを処理ロジックから抽象化します。 実装を切り替えることで、同じ検証コードと生成コードを異なるプラットフォームで実行できるようになります。

  • .NET MAUIは、モバイル受信ステーションとして使用されるAndroidおよびiOSタブレット向けに、カメラベースのスキャナ実装を提供します。
  • Blazor Server は、同じ BarcodeScanned イベントにフィードするJavaScriptカメラアクセスを使用したブラウザベースのスキャンをサポートしています
  • AndroidおよびiOSネイティブ実装により、モバイル開発者はバックエンドで同じIronBarcodeデコーダを使用してカメラスキャン機能を利用できるようになります。

クラウドネイティブアーキテクチャの場合、検証およびラベル生成の手順は、キューメッセージによってトリガーされるAzure Functionsとして実行でき、デスクトップアプリケーションはスキャナの入力ゲートウェイとしてのみ機能します。 この分離は、コンプライアンス監査のためにラベル印刷ロジックを一元化する必要がある場合に特に有効です。

次のステップは何ですか?

IronBarcodeを使用してUSBバーコードスキャナアプリケーションを構築するには、4つの具体的な段階があります。バーストタイミング検出によるキーボード入力の取得、IronBarcodeデコーダによるスキャン値の検証、必要な形式での応答ラベルの生成、そして同時キューによる大量のスキャン処理です。 各段階は独立しており、個別にテスト可能です。

ここから、バッチ処理シナリオ向けのマルチバーコード読み取り機能、画像ベースの入力向けのクロップ領域最適化機能、または旧型の倉庫機器向けのMSIバーコードサポートなど、アプリケーションの拡張を検討してください。 IronBarcodeのドキュメントには、サポートされているすべてのフォーマットと、高度なリーダー設定オプションが記載されています。

無料トライアルを開始して開発ライセンスキーを取得し、今すぐIronBarcodeをスキャンアプリケーションに統合しましょう。

よくある質問

IronBarcodeとは何であり、それはUSBバーコードスキャナーとどのように関連していますか?

IronBarcodeは、USBバーコードスキャニングのための堅牢なC#アプリケーションを開発者が構築できるライブラリです。バーコード検証、データ抽出、およびバーコード生成などの機能を提供します。

IronBarcodeはUSBスキャナーからのバーコードデータを検証できますか?

はい、IronBarcodeは、USBスキャナーからキャプチャしたバーコードデータを検証し、C#アプリケーションのデータ整合性と精度を確保します。

IronBarcodeはバーコード生成をどのように扱っていますか?

IronBarcodeは、新しいバーコードをオンザフライで生成し、C#アプリケーション内で簡単にバーコードを作成し印刷できるようにします。

IronBarcodeにはUSBバーコードスキャニング用のエラーハンドリングサポートがありますか?

はい、IronBarcodeには、USBバーコードスキャニングと処理中に発生する一般的な問題を管理するための包括的なエラーハンドリングが含まれています。

IronBarcodeを使用してスキャンできるバーコードの種類は何ですか?

IronBarcodeはQRコード、UPC、Code 39など、多くのバーコードシンボロジーのスキャニングをサポートしており、さまざまなアプリケーションにおいて多用途です。

IronBarcodeはスキャンされたバーコードから構造化情報を抽出できますか?

はい、IronBarcodeはスキャンされたバーコードから構造化情報を抽出し、効率的なデータ処理と管理を支援します。

C#でUSBバーコードスキャナーアプリケーションの構築を始めるにはどうすればいいですか?

C#でUSBバーコードスキャナーアプリケーションの構築を始めるには、IronBarcodeと提供されたコード例とドキュメントを利用し、開発プロセスをガイドにします。

Jordi Bardia
ソフトウェアエンジニア
Jordiは、最も得意な言語がPython、C#、C++であり、Iron Softwareでそのスキルを発揮していない時は、ゲームプログラミングをしています。製品テスト、製品開発、研究の責任を分担し、Jordiは継続的な製品改善において多大な価値を追加しています。この多様な経験は彼を挑戦させ続け、興味を持たせており、Iron Softwareで働くことの好きな側面の一つだと言います。Jordiはフロリダ州マイアミで育ち、フロリダ大学でコンピュータサイエンスと統計学を学びました。

アイアンサポートチーム

私たちは週5日、24時間オンラインで対応しています。
チャット
メール
電話してね