Windows 在不了解VID的情况下读取USB HID条形码扫描仪输入&;PID

Windows 在不了解VID的情况下读取USB HID条形码扫描仪输入&;PID,windows,winapi,64-bit,barcode,barcode-scanner,Windows,Winapi,64 Bit,Barcode,Barcode Scanner,我正在尝试为条形码扫描仪开发独立于设备的库,它必须在windows环境下工作 我在这个领域做过一些研究,据我所知,这个问题的大多数解决方案都依赖于特定的设备VID&PID(RawInput@filter by VID&PID string),在我的情况下,这是不可接受的,因为我正在尝试开发一个与设备无关的解决方案,它将与任何USB条形码扫描仪一起工作 事实上,这件事很有挑战性,至少对我来说,这里有一些确切的要求。此外,我不能要求用户热插拔设备(在这种情况下,我可以只检测插入的设备并将其提取为vi

我正在尝试为条形码扫描仪开发独立于设备的库,它必须在windows环境下工作

我在这个领域做过一些研究,据我所知,这个问题的大多数解决方案都依赖于特定的设备VID&PID(RawInput@filter by VID&PID string),在我的情况下,这是不可接受的,因为我正在尝试开发一个与设备无关的解决方案,它将与任何USB条形码扫描仪一起工作

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

此外,我不能以任何方式重新编程条形码扫描仪,除非它从我的程序完成(也许我可以发送一些条形码扫描仪特定的IOCTL,这将使它回答我?)

目前,我将使用此问题中提出的解决方案:

此外,我还看到了商业图书馆(当然,它没有任何来源,也没有关于它如何实现的任何信息,但考虑到他们的变更日志中有“性能计数器”一词,我猜他们在上面的链接中使用了解决方案),它实现了这一功能,但在x64系统中不起作用。可能是因为凌乱的代码,也可能是因为它可能使用了某种过滤器(迷你)驱动程序。它被加密了,我无法重新分发

我的确切问题是: 有没有办法确定这个HID键盘实际上不是一个键盘,而是一个条形码扫描仪?我在Win7x64上看到它是作为条形码扫描仪连接的,而不是键盘(这是一个系统错误,或者说是某种)

正是我现在所做的:

  • 通过RID_INPUTSINK读取输入
  • 通过设备的vid和pid识别所有输入
  • 当VK_ENTER显示在缓冲区上时,将所有输入置于单独的缓冲区并从缓冲区收集条形码
  • 我现在要做的是:

  • 通过RID\U INPUTSINK读取输入
  • 启动特定设备的计时器,如果下一个符号为VK_,则输入-停止计时器
  • 如果计时器超过50毫秒的限制-关闭它,并删除所有进一步的设备输入
  • 如果设备将成功读取从第一个符号到VK_的字符序列,输入-提取设备VID和PID/句柄,并以更方便的方式使用它(无需计时)

    我在C++上开发,纯WinAPI,它是一个DLL库,并在X32-86X3X64和X32-64架构中工作在WindowsXP、Vista、7, 8。p> 更新0: 刚刚发现条形码扫描仪有自己的用法页面和USB规格中的用法:


    根据本文件,USB条形码扫描仪的用途为0x8C页和0x02页。不幸的是,我无法将其用作RAWINPUTDEVICE.dwUsage和RAWINPUTDEVICE.dwUsagePage。可能是因为系统在上面安装了usb键盘驱动程序,在用户模式下,它和真正的usb键盘并没有什么区别。这些值可能在kernelmode环境中可用(选项之一是开发hid筛选器驱动程序)。

    这并不能回答您的特定问题,但无论如何

    一年多以前,我在更恶劣的环境下实现了条形码阅读器支持。它用于报告应用程序,与纯Java(主要在Windows上的跨平台富客户端)中的后勤数据关联。 我也发现了你所说的键盘驱动程序,它阻止了在用户模式下区分实际的USB设备,至少乍一看是这样。还有一些更昂贵的设备有自己的驱动程序和先进的功能,这将允许某种区别。 我在该环境中遇到的所有条形码阅读器都可以作为键盘显示,用于简单地填充SAP表单字段并按下enter键,这是一种常见情况。可使用“魔术条形码”或其他制造商特定方法配置终端

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

    • 由前两个字符确定的按键冲程频率(初始/超时后)
    • 抖动(频率/速率变化)
    • 有效字符集
    • 终止线路中断
    输入被缓冲区消耗,直到不满足机器生成输入的条件,或者验证通过,条形码侦听器将收到通知。在任何一种情况下,输入都可以被转发,就好像没有其他事情发生一样

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


    编辑:

    刚刚挖出Java源代码;我可以给你早期修改代码的例子作为一个例子(没有保修,也考虑实施CR):

    import java.awt.event.KeyEvent;
    导入java.awt.event.KeyListener;
    导入java.util.regex.Matcher;
    导入java.util.regex.Pattern;
    /**
    *条形码阅读器的{@link keylister}实现。此实现
    *检查输入速率和抖动,以区分人和扫描仪
    *按“精度”输入序列。来自扫描仪的条形码输入序列是
    *通常以换行符终止。
    * 
    *@作者:我
    */
    公共抽象类AbstractBarcodeInputListener实现了KeyListener{
    公共静态最终整数默认值\u最小\u暂停=300;/[ms]
    公共静态最终整数默认值\u最大\u时间\u增量=200;/[ms]
    公共静态最终整数默认值\u最大\u时间\u抖动=50;/[ms]
    公共静态整数parseInt(模式、int组、字符串行){
    最终匹配器匹配器=模式匹配器(线);
    if(matcher.matches())
    
    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);
    }