不知道 VID 和 PID 的情况下读取 USB HID 条码扫描器的输入

9
我正在尝试开发一个设备无关的条码扫描器库,它必须在Windows环境下工作。
我在这个领域做了一些研究,据我所知,大多数解决此问题的方法都依赖于特定设备的VID和PID(在过滤器中使用RawInput @按vid&pid字符串过滤),在我的情况下,这是不可接受的,因为我正在尝试开发一个设备无关的解决方案,可以与任何USB条码扫描器配合使用。
实际上,这件事对我来说相当具有挑战性,以下是确切要求。而且我不能要求用户热插拔设备(在那种情况下,我可以检测到已插入的设备并提取它的vid/pid)。也不能使用设备的VID&PID数据库。总体来说,我实际上不能使用vid&pid。
也不能以任何方式重新编程条码扫描仪,除非从我的程序中完成(也许我可以发送一些条形码扫描仪特定的IOCTLs,使其回答我?)。
目前我将使用此问题中提出的解决方案: Reading a barcode using a USB barcode scanner along with ignoring keyboard data input while scanner product id and vendor id are not known 我还看到了一款商业库(当然没有任何源代码和有关它如何实现的信息,但是考虑到它们在变更日志中使用了一些单词“性能计数器”,我猜测他们使用了上面链接中的解决方案),但它不适用于x64系统。可能是因为代码混乱或者它可能使用了某种过滤(mini)驱动程序。它是加密的,我不能再分发它。
我的确切问题是: 是否有办法确定这个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/句柄,并以更方便的方式处理它(无需计时器)。
我正在使用 C++ 和纯 WinAPI 进行开发,它将成为一个 DLL 库,并能在 Windows XP、Vista、7、8 上的 x32-86 和 x32-64 架构上运行。
更新 0: 刚才发现条形码扫描仪在 USB 规范中有自己的使用页面和用途: http://www.usb.org/developers/devclass_docs/pos1_02.pdf 根据这个文档,USB 条形码扫描器具有 UsagePage 0x8C 和 Usage 0x02。不幸的是,我未能将其用作 RAWINPUTDEVICE.dwUsage 和 RAWINPUTDEVICE.dwUsagePage。可能因为系统安装了其 USB 键盘驱动程序,并且在用户模式下无法区分它与真实的 USB 键盘。也许这些值在内核模式环境中可用(其中一个选项是开发 HID 过滤驱动程序)。

你好,你能提供上述实现的解决方案吗? - SVG
1个回答

24

虽然这不能回答您特定的问题,但是......

一年多以前,我在更艰难的环境下实现了条形码读取器支持。这是一个与物流数据相关联的报表应用程序,在纯Java中编写(跨平台的丰富客户端,主要在Windows上)。 我发现了你所说的与键盘驱动程序有关的相同问题,它防止在用户模式下区分实际的USB设备,至少乍一看是这样的。有一些价格更高的设备具有自己的驱动程序和高级功能,这将允许某种形式的区分。 我在那个环境中遇到的所有条码读取器都可见为键盘,并用于简单地填写SAP表单字段并按下回车键,这是一个常见情况。可以使用“魔术条形码”或另一种制造商特定的方法配置终止方式。

因此,决定不采用任何基于JNI的平台特定实现。 相反,我还使用拦截风格的方法(扩展版)来评估在特定Swing / AWT表单中使用通用键盘输入的内容,其中包括以下标准:

  • 由前两个字符确定的击键频率(初始/超时后)
  • 抖动(频率/速率变化)
  • 有效字符集
  • 终止换行符。

输入将被缓冲区使用,直到不满足机器生成输入的标准,或已通过验证并通知条形码侦听器。在任何情况下,输入都可以转发,就好像什么也没有发生一样。

这被证明非常准确,因为对于人类来说,以(几乎)零抖动的条形码读取器速率输入有效序列是几乎不可能的。


编辑:

刚才找到了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);
}

5
没有人发表评论,我真的认为这值得一个“哇!” - Paul-Sebastian Manole
好的,我认为你至少应该向我们展示如何实现这段代码。 - Francis Nduba Numbi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接