フッターコンテンツにスキップ
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();
}
$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" }
        }
    };
}
$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";
$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; } = "";
}
$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');
}
$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;
    }
}
$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);
    }
}
$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時間オンラインで対応しています。
チャット
メール
電話してね