ちょっとバーコードリーダーをさわる機会があって備忘録的にメモをしておく。
バーコードリーダーは物流の商品チェックなどに使われててセルフレジなどでも使ったことがあることと思います。
バーコードリーダーを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メッセージの追跡に結構重宝すると思います。