キャンペーン


2013年7月1日月曜日

バーコードリーダーの制御方法 (USB/HID編)

ちょっとバーコードリーダーをさわる機会があって備忘録的にメモをしておく。

バーコードリーダーは物流の商品チェックなどに使われててセルフレジなどでも使ったことがあることと思います。

バーコードリーダーをPCに繋げてシステム利用する場合のテクニカルメモです。



モノ自体は秋葉に行けば新品で5000円程度で購入可能です。

ちょっと体験したいだけならジャンク品や中古品で試してみる事です。

運がよければ数百円から入手可能です。


バーコードリーダの機能は至って単純で、バーコードを読み取ってテキスト文字に変換するだけの機能しかもっていません。

つまりPCにつなげてしまえばキーボードと同じ扱いになります(バーコードで読んだものしか打てないキーボードですが、、、)。

今はUSBタイプのものが主流ですが一昔前はRS232タイプのものが主流でした。

RS232Cタイプのものはつないでデバイスを認識させただけではバーコードリーダーとして機能しません。プログラムを書いて制御する必要があります。

もちろんUSBタイプのCOMポートタイプもあります。USBで認識させた後、HIDもしくはシリアルと選択できるタイプも有るようです。

キーボードやマウスも以前はPS/2が主流でしたが今はUSBなのと同じです。

そこでIT技術者が知っておかなけれならないことは、キーボードの取り扱いをUSBオンリーで捉えてなならないということです。

WindowsであればHID(ヒューマン・インターフェースデバイス)という観点で考える必要があります。

デバイスマネージャで見たことのある名前だと思います。


このHIDデバイスというのはかなり便利なプロトコルでマウスでも使われています。


ですから周辺入力デバイスをPCでトラップして処理する場合はUSB云々よりこのHIDデバイスであるかどうかが鍵になります。

バーコードリーダーはメーカーごとに仕様はマチマチです。

メーカーをチョイスするときにこのHIDであるかどうか、シリアル通信にするかが肝になります。

このHID準拠になっていればWindowsであれば基本的にウィンドウメッセージで処理できるので、あとはキーボードメッセージもしくはマウスメッセージをハンドリングすればバーコードリーダを簡単に制御できるのです。

そもそもバーコードリーダーは入力可能なテキストボックスにカーソルがあればバーコードから読み取った文字が自動に画面上のカーソル位置に入力されます。



じゃ、何もしなくてもシステムを作れるじゃないかと素人は思ってしまうのだが、実はこのバーコードリーダーから来た文字を如何にハンドリングするかという点が肝になってきます

WindowsでVB.NETがあればキーイベントをハンドリングしてやれば簡単じゃないか!と思いますよね?!

でもそうは問屋が卸さないんです。

何故なら、キーボードから打った文字なのかバーコードから読み取って送られてきた文字なのかVBのキーイベントからではわからないんです。

だってバーコードから打った文字が入力されてはいけないシーンてありますよね?
それを制限したいじゃないですか…

じゃあどうするかというと方法はない訳でありません。

ウィンドウメッセージのWM_CHARを追跡すれば可能です。


一昔前まではWindowsのプログラムはCで書いていた時代ですが今はほとんど高級言語です。

Windowsがどのように動いているのかも知らない技術者が増えてきたように感じます。

それはさておき、Windowメッセージの概念を知らないとある意味Windowsの醍醐味を全然知らずにいるのと同じです。

実はVB.NETでもWindowsメッセージを追跡できる仕組みがあります。

というかウィンドウプロシージャを書けるんです。

ウィンドウクラスに以下のようなハンドラをオーバーライドします。


    Protected Overrides Sub WndProc(ByRef m As Message)
     If m.Msg = WM_CHAR Then
            ' ここにWM_CHARメッセージが来た時の処理を書く
     End if 
        MyBase.WndProc(m)
    End Sub

CでWindowsプログラムと同じですね。

ところがです…。


なぜかフォームにテキストボックスをおき、フォームのクラスに上記ハンドラをオーバーライドしてもWM_CHARが来ないんです。

何故だと思いますか?

この理由に気づいてないで悩んでらっしゃる方が多いんです。

つまりVBしか知らないからです。

そもそも論としてウィンドウメッセージを知ってば原因はすぐ見当がつきます。


結論から言います。

テキストボックス(TextBox)クラスを継承したクラスを1つ作り、そこにウィンドウプロシージャをオバーライドします。

これできちんとバーコードリーダーいやキーボードからのメッセージが飛んできます。
以下のようなテキストボックスクラスを作ってやります。
Public Class BarcodeTextBox
    Inherits TextBox

    Private _start As Boolean = False
    Protected Overrides Sub WndProc(ByRef m As Message)
        Const WM_CHAR As Integer = &H102


        If m.Msg = WM_CHAR Then

        End If
        MyBase.WndProc(m)
    End Sub

End Class

これをフォームのデザイナ内でTextBoxをBarcodeTextBoxに置き換えてやればロジックも弄らず拡張できるんです。

このようなやり方をサブクラス化と言います。


話がそれましたが、結局バーコードリーダとキーボードの識別はどうすればいいかを説明していませんでした。

実はバーコードリーダーの設定を変更しないといけません。

デフォルトでは通常CR+LFがバーコードリーダで読み取った文字に付加されて出荷されてることが多いのですが、これでは難しいです。

なぜ難しいかというとWM_CHARメッセージは1文字単位で文字を追跡するメッセージです。

キーボードもバーコードリーダーも同じキーコードが来ます。

別にキーボード2台PCにつけて考えても同じです。

その2台のキーボードの判別をWM_CHARでは識別不能です。

そこでバーコードリーダにもよるのですがSTX、ETXコードを読み取り文字の前後に付加する機能が実装されています。

たとえば12345というもじであれば

STX+12345+ETX のコードがWindowsへ送られます。

実際STXはキャラクターコード0x2、ETXは0x3です。

この開始コードから終端コードをWM_CHARで追跡するように処理すればバーコードリーダーから来たかどうかのハンドリングが可能になるわけです。

最後に実例サンプル(BarcodeTextBoxクラス)を添付して締めくくります。完璧なソースではないのであとはこれをアレンジすれば使える処理が書けると思います。

Imports System.ComponentModel
Public Class BarcodeTextBox
    Inherits TextBox

    Private _buf As String = ""

    Event OnBarcodeComplete()

    Private _start As Boolean = False
    Protected Overrides Sub WndProc(ByRef m As Message)
        Const WM_CHAR As Integer = &H102


        If m.Msg = WM_CHAR Then

            If _BarcodeUse Then
                'バーコード使用モード
                If m.WParam = &H2 Then  'STX
                    _start = True
                    _buf = ""
                    Me.Text = ""
                ElseIf m.WParam = &H3 Then  'ETX
                    'Debug.Print("WM_CHAR => " & _buf)
                    If _start Then
                        Me.Text = _buf
                        RaiseEvent OnBarcodeComplete() 'エベント発砲
                    End If
                    _buf = ""
                    _start = False
                    Return
                Else
                    'Debug.Print("WM_CHAR[0] => " & m.WParam.ToString)
                    If _start Then
                        'barcode開始済の場合
                        _buf &= Chr(m.WParam)
                        If _start Then Return

                    Else
                        'barcode開始してない場合
                        'Return
                    End If
                End If

            Else
                'バーコード受付不可モード ⇒ キーボードの入力のみ
                If m.WParam = &H2 Then  'STX

               _start = True
                    _buf = ""
                ElseIf m.WParam = &H3 Then  'ETX
                    _start = False
                    _buf = ""
                Else
                    'Debug.Print("WM_CHAR[0] => " & m.WParam.ToString)
                    '_buf &= Chr(m.WParam)
                    If _start Then
                        Return
                    Else
                        'Return
                    End If
                End If
            End If


        End If
        MyBase.WndProc(m)
    End Sub


    Private _BarcodeUse As Boolean = False
    <Browsable(True)> _
    Public Property BarcodeUse() As Boolean
        Get
            Return (_BarcodeUse)
        End Get
        Set(ByVal value As Boolean)
            _BarcodeUse = value
        End Set
    End Property

End Class


なお上記は以下の様な機能を施しています

  • BarcodeUseプロパティにTrueにした場合にバーコードリーダーから読み取れることができる。
  • BarcodeUseプロパティにFalseにした場合にバーコードリーダーからの文字は受け付けない。
  • BarcodeUseプロパティの状態によらずキーボードからの入力ができる。
  • OnBarcodeCompleteイベントでバーコードリダーの入力完了イベントをトラップできる

あくまで上記はHID準拠の場合であってこれより緻密に処理したい場合(バックグラウンドで処理するなど)はシリアル通信タイプのバーコードリーダを用いてCOMポートをI/Oするしかないでしょう!また機会があればご報告します。

最後に補足ですが、VisualStudioに付属するツールのSpyがWindowsメッセージの追跡に結構重宝すると思います。


1 件のコメント:

  1. 実際に使用するには、どのようにしたらよいですか?
    すいません。素人なのでよくわかりません。
    よろしくお願いします

    返信削除