在不知道 VID&PID 的情况下读取 USB HID 条码扫描仪输入 [英] Reading USB HID barcode scanner input without knowing VID&PID

查看:41
本文介绍了在不知道 VID&PID 的情况下读取 USB HID 条码扫描仪输入的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试为条码扫描器开发独立于设备的库,它必须在 Windows 环境中工作.

I'm trying to develop device independent library for barcode scanners, it has to be working in windows environment.

我在这个领域做了一些研究,afaik 这个问题的大部分解决方案都取决于特定的设备 VID&PID(RawInput @ filter by vid&pid string),在我的情况下这是不可接受的,因为我我试图开发一个独立于设备的解决方案,它将与任何 USB 条形码扫描仪一起使用.

I've done some research in this field, afaik most of the solutions of this problem are depending on specific device VID&PID (RawInput @ filter by vid&pid string), in my situation this is inacceptable, because i'm trying to develop a device independent solution, which will be working with any USB-barcode scanner.

其实这个东西很有挑战性,至少对我来说,这里有确切的要求.此外,我不能要求用户热插拔设备(在这种情况下,我可以只检测插入的设备并提取它的 vid/pid).我也不能使用设备的 VID&PID 数据库.一般来说,我实际上根本无法使用 vid&pid.

Actually this thing is quite challenging, for me atleast, here are exact requiriments. Also i can't ask user to hot-plug device (in that case i could've just detect plugged device and extract it vid/pid). Also i can't use VID&PID database of devices. In general i can't use vid&pid at all actually.

此外,我不能以任何方式重新编程条码扫描器,除非它是从我的程序中完成的(也许我可以发送一些特定于条码扫描器的 IOCTL 使其回复我?).

Also i can't in any way reprogramm barcode scanner, unless it's done from my programm (maybe i can send some barcodescanner-specific IOCTLs which will make it answer to me?).

目前我将使用在这个问题中提出的解决方案:使用 USB 读取条码条码扫描仪以及忽略键盘数据输入,而扫描仪产品 ID 和供应商 ID 未知

Currently i'm going to use solution proposed in this question: 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 系统中不起作用.可能是因为代码混乱,或者因为它可能使用某种过滤器(迷你)驱动程序.它是加密的,我无法重新分发它.

Also i have seen commercial library (which is offcourse comes without any sources and any info about how it's implemented, but considering they have'd some word "Perfomance counter" in their changelogs, i guess they used solution in the link above), which implements this functionality, but it doesn't work in x64 systems. Probably either because of messy code or beacause it probably uses some kind of filter (mini) driver. It's crypted and i can't redistribute it.

我的确切问题是:有什么办法可以确定这个HID键盘实际上不是键盘,而是条码扫描仪?我在 Win 7 x64 上看到它连接为条码扫描仪,而不是键盘(这是一个系统错误,或者类似的问题).

My exact question is: Is there any way to determine that this HID keyboard is in fact not a keyboard, but a barcode scanner? I've seen on Win 7 x64 that it connects as Barcode scanner, not keyboard (this was a system bug, or sort of).

我现在在做什么:

  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 库,并且可以在 x32-86 和 x32-64 架构上的 Windows XP、Vista、7、8 中工作.

I'm developng it on C++, pure WinAPI, it will be a DLL library, and got to work in Windows XP, Vista, 7, 8 on x32-86 and x32-64 architectures.

更新 0:刚刚发现条码扫描器在USB规范中有自己的用法和用法:http://www.usb.org/developers/devclass_docs/pos1_02.pdf

UPDATE 0: Just found that barcode scanner have their own usagePage and usage in USB specs: http://www.usb.org/developers/devclass_docs/pos1_02.pdf

根据本文档,USB 条码扫描器具有 UsagePage 0x8C 和 Usage 0x02.不幸的是,我未能将其用作 RAWINPUTDEVICE.dwUsage 和 RAWINPUTDEVICE.dwUsagePage.可能是因为系统在它上面安装了 USB 键盘驱动程序,并且在用户模式下它与真正的 USB 键盘无法区分.可能这些值在内核模式环境中可用(选项之一是开发 hid 过滤器驱动程序).

According to this document USB Barcode scanner have UsagePage 0x8C and Usage 0x02. Unfortunately i've failed using it as RAWINPUTDEVICE.dwUsage and RAWINPUTDEVICE.dwUsagePage. Probably because system install it's usb keyboard driver on top of it and in user mode it is indistinguishable from real usb keyboard. Probably those values are usable in kernelmode environment (one of the options is to develop hid filter driver).

推荐答案

这不能回答您的具体问题,但无论如何...

This does not answer your specific question, but anyway...

一年多以前,我在更不利的情况下实施了条码阅读器支持.它用于与纯 Java 中的后勤数据相关联的报告应用程序(跨平台富客户端,主要在 Windows 上).我发现您对键盘驱动程序的看法相同,它阻止了用户模式下实际 USB 设备的区分,至少乍一看是这样.有更昂贵的设备有自己的驱动程序和高级功能,这将允许某种区别.我在那个环境中遇到的所有条码阅读器都作为键盘可见,过去只是简单地填写 SAP 表单字段并按回车键,这是一种常见的情况.终止可以使用魔术条形码"或其他制造商特定方法进行配置.

More than a year ago, I implemented barcode reader support under even more adverse circumstances. It was for a reporting applicaton with association to logistical data in pure Java (cross platform rich client, primarily on Windows). I found out the same you're saying about the keyboard driver, which prevents distinction of actual USB devices in user mode, at least at the first glance. There are more expensive devices with own drivers and advanced features, which would allow some sort of distinction. All barcode readers I encountered in that environment were visible as keyboards and used to simply fill an SAP form field and hit the enter key, which is a common case. The termination may be configurable using 'magic barcodes' or another manufacturer specific method.

因此,该决定反对任何基于 JNI 的、特定于平台的实现.相反,我还使用以下标准评估了某些 Swing/AWT 表单中的通用键盘输入,从而实现了一种类似拦截的方法(您的扩展版本):

So the decision was against any JNI based, platform specific implementation. Instead, I implemented also an interception-like approach (extended version of yours) by evaluating generic keyoard input within certain Swing/AWT forms using these criteria:

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

输入被缓冲区消耗,直到不满足机器生成输入的标准,或者验证通过,条形码侦听器将收到通知.在任何一种情况下,输入都可以被转发,就好像没有其他事情发生一样.

The input gets consumed by a buffer until the criteria for machine generated input aren't met, or the validation has been passed, where barcode listeners will be notified. In either situation, the input can be forwarded as if nothing else happened.

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

This has proven to be very accurate, since for a human, it's all but impossible to enter a valid sequence at the barcode reader's rate with (almost) zero jitter.

刚挖出Java源码;我可以给你上面实现的一个早期版本的代码作为例子(不保证,也考虑实现CR):

Just dug out the Java source; I can give you the code of an early revision of the implementation above as an example (no warranty, also consider to implement 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 '':
                this.clearInput();
                break;
            case '
':
                this.commitInput(e);
                break;
            default:
                this.input += c;
                break;
            }
        } else {
            this.clearInput();
        }
    }

    public abstract void codeRead(String line);
}

这篇关于在不知道 VID&amp;PID 的情况下读取 USB HID 条码扫描仪输入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆