9

バーコードスキャナー用のデバイスに依存しないライブラリを開発しようとしています。Windows環境で動作する必要があります。

私はこの分野でいくつかの調査を行いましたが、この問題の解決策のほとんどは特定のデバイスに依存していますVID&PID(RawInput @ filter by vid&pid string)、私の状況では、デバイスに依存しないデバイスを開発しようとしているため、これは受け入れられませんソリューション。USBバーコードスキャナーで動作します。

実際、これは非常に難しいことです。少なくとも私にとっては、正確な要件があります。また、ユーザーにデバイスをホットプラグするように依頼することはできません(その場合、プラグされたデバイスを検出してvid / pidで抽出することができます)。また、デバイスのVID&PIDデータベースを使用できません。一般的に、私は実際にはvid&pidをまったく使用できません。

また、プログラムから実行しない限り、バーコードスキャナーを再プログラムすることはできません(おそらく、バーコードスキャナー固有のIOCTLを送信して、応答することができますか?)。

現在、この質問で提案されているソリューションを使用し ます。スキャナーの製品IDとベンダーIDが不明な場合に、キーボードデータ入力を無視してUSBバーコードスキャナーを使用してバーコードを読み取る

また、商用ライブラリ(ソースや実装方法に関する情報がないオフコースですが、変更ログに「パフォーマンスカウンター」という単語が含まれていることを考えると、上記のリンクのソリューションを使用したと思います)を見たことがあります。この機能を実装していますが、x64システムでは機能しません。おそらく、コードが乱雑であるか、何らかのフィルター(ミニ)ドライバーを使用しているためです。暗号化されており、再配布できません。

私の正確な質問は 、このHIDキーボードが実際にはキーボードではなく、バーコードスキャナーであると判断する方法はありますか?Win 7 x64で、キーボードではなくバーコードスキャナーとして接続することを確認しました(これはシステムのバグ、または一種でした)。

まさに私が今していること:

  1. RID_INPUTSINKによる入力の読み取り。
  2. デバイスのvid&pidによってすべての入力を区別する
  3. VK_ENTERがバッファに表示されたときに、すべての入力を個別のバッファに入れ、バッファからバーコードを収集します。

私が現在やろうとしていること:

  1. RID_INPUTSINKによる入力の読み取り
  2. 特定のデバイスのタイマーを開始し、次のシンボルがVK_ENTERの場合-タイマーを停止します
  3. タイマーが50ミリ秒の制限を超えた場合は、タイマーをオフにして、それ以降のすべてのデバイス入力をドロップします。
  4. デバイスが最初のシンボルからVK_ENTERまでの文字シーケンスを正常に読み取る場合-デバイスのVID&PID / handleを抽出し、より便利な方法で(タイマーなしで)操作します。

私はそれをC++、純粋なWinAPIで開発しており、DLLライブラリになり、x32-86およびx32-64アーキテクチャ上のWindows XP、Vista、7、8で動作するようになります。

更新0: バーコードスキャナーには独自のusagePageとUSB仕様での使用法があることがわかりました: http ://www.usb.org/developers/devclass_docs/pos1_02.pdf

このドキュメントによると、USBバーコードスキャナーにはUsagePage0x8CとUsage0x02があります。残念ながら、RAWINPUTDEVICE.dwUsageおよびRAWINPUTDEVICE.dwUsagePageとして使用できませんでした。おそらくシステムがその上にusbキーボードドライバーをインストールし、ユーザーモードでは実際のusbキーボードと見分けがつかないためです。おそらく、これらの値はカーネルモード環境で使用できます(オプションの1つは、hidフィルタードライバーを開発することです)。

4

1 に答える 1

24

これはあなたの特定の質問に答えませんが、とにかく...

1年以上前、私はさらに厳しい状況下でバーコードリーダーのサポートを実装しました。これは、純粋なJava(クロスプラットフォームのリッチクライアント、主にWindows)のロジスティクスデータに関連付けられたレポートアプリケーション用でした。少なくとも一見したところ、ユーザーモードで実際のUSBデバイスを区別できないキーボードドライバーについてあなたが言っているのと同じことがわかりました。独自のドライバーと高度な機能を備えたより高価なデバイスがあり、これにより、ある種の区別が可能になります。その環境で遭遇したすべてのバーコードリーダーはキーボードとして表示され、SAPフォームフィールドに入力してEnterキーを押すだけでした。これは一般的なケースです。終端は、「マジックバーコード」または別のメーカー固有の方法を使用して構成できます。

したがって、この決定は、JNIベースのプラットフォーム固有の実装に反対しました。代わりに、次の基準を使用して特定のSwing / AWTフォーム内の一般的なkeyoard入力を評価することにより、インターセプトのようなアプローチ(拡張バージョン)も実装しました。

  • 最初の2文字で決定されるキーストロークの頻度(最初/タイムアウト後)
  • ジッタ(周波数/レート変化)
  • 有効な文字のセット
  • 改行を終了します。

入力は、マシンで生成された入力の基準が満たされないか、検証に合格するまでバッファによって消費されます。検証に合格すると、バーコードリスナーに通知されます。どちらの状況でも、入力は他に何も起こらなかったかのように転送できます。

これは非常に正確であることが証明されています。なぜなら、人間の場合、(ほぼ)ジッターがゼロのバーコードリーダーの速度で有効なシーケンスを入力することはほとんど不可能だからです。


編集:

Javaソースを掘り出しました。例として、上記の実装の早期リビジョンのコードを提供できます(保証なし、CRの実装も検討してください)。

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * A {@link KeyListener} implementation for barcode readers. This implementation
 * checks for input rate and jitter to distinguish human and scanner
 * input sequences by 'precision'. A barcode input sequence from a scanner is
 * typically terminated with a line break.
 * 
 * @author Me
 */
public abstract class AbstractBarcodeInputListener implements KeyListener {
    public static final int DEFAULT_MIN_PAUSE = 300;// [ms]
    public static final int DEFAULT_MAX_TIME_DELTA = 200;// [ms]
    public static final int DEFAULT_MAX_TIME_JITTER = 50;// [ms]

    public static Integer parseInt(Pattern pattern, int group, String line) {
        final Matcher matcher = pattern.matcher(line);
        if (matcher.matches())
            return Integer.parseInt(matcher.group(group));
        return null;
    }

    private String input;

    private final long minPause;
    private long maxTimeDelta;
    private final long maxTimeJitter;

    private long firstTime;
    private long firstTimeDelta;
    private long lastTimeDelta;
    private long lastTime;

    public AbstractBarcodeInputListener(long maxTimeDelta, long maxTimeJitter) {
        this.input = new String();

        this.minPause = AbstractBarcodeInputListener.DEFAULT_MIN_PAUSE;
        this.maxTimeDelta = maxTimeDelta;
        this.maxTimeJitter = maxTimeJitter;

        this.firstTime = 0;
        this.firstTimeDelta = 0;
        this.lastTimeDelta = 0;
        this.lastTime = 0;
    }

    public AbstractBarcodeInputListener() {
        this(AbstractBarcodeInputListener.DEFAULT_MAX_TIME_DELTA,
                AbstractBarcodeInputListener.DEFAULT_MAX_TIME_JITTER);
    }

    private boolean checkTiming(KeyEvent e) {
        final int inputLength = this.input.length();
        final long time = e.getWhen();
        long timeDelta = time - this.lastTime;
        long absJitter = 0;
        long relJitter = 0;

        boolean inputOK = true;

        switch (inputLength) {
        case 0: // pause check
            inputOK &= (timeDelta > this.minPause);
            this.firstTime = time;
            this.firstTimeDelta = timeDelta = 0;
            break;
        case 1: // delta check
            this.firstTimeDelta = timeDelta;
            inputOK &= (timeDelta < this.maxTimeDelta);
            break;
        default:// jitter check & delta check
            absJitter = Math.abs(timeDelta - this.firstTimeDelta);
            relJitter = Math.abs(timeDelta - this.lastTimeDelta);
            inputOK &= (absJitter < this.maxTimeJitter);
            inputOK &= (relJitter < this.maxTimeJitter);
            inputOK &= (timeDelta < this.maxTimeDelta);
            break;
        }

        this.lastTime = time;
        this.lastTimeDelta = timeDelta;

        return inputOK;
    }

    @Override
    public void keyPressed(KeyEvent e) {
    }

    private void clearInput() {
        this.input = new String();
    }

    private void commitInput(KeyEvent e) {
        final String code = this.input;
        if (!code.isEmpty()) {
            final long avgIntervalTime = e.getWhen() - this.firstTime;
            this.maxTimeDelta = (avgIntervalTime * 15) / 10;
            this.clearInput();
            this.codeRead(code);
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {
    }

    @Override
    public void keyTyped(KeyEvent e) {
        if (this.checkTiming(e)) {
            final char c = e.getKeyChar();
            switch (c) {
            case '\b':
                this.clearInput();
                break;
            case '\n':
                this.commitInput(e);
                break;
            default:
                this.input += c;
                break;
            }
        } else {
            this.clearInput();
        }
    }

    public abstract void codeRead(String line);
}
于 2012-12-31T22:23:54.767 に答える