我试图开发条形码扫描仪的设备独立库,它必须在Windows环境中工作。
我已经在这个领域做了一些研究,afaik这个问题的大部分解决scheme都取决于特定的设备VID&PID(RawInput @ filter by vid&pid string),在我的情况下这是不可接受的,因为我试图开发一个独立于设备解决scheme,这将与任何USB条形码扫描仪。
其实这件事情相当具有挑战性,至less对我来说,这是确切的要求。 另外我不能要求用户热插拔设备(在这种情况下,我可能只是检测插入设备,并提取它的VID / PID)。 另外我不能使用设备的VID和PID数据库。 一般来说,我实际上不能使用vid&pid。
我也不能以任何方式重新编程条码扫描器,除非它是从我的程序完成(也许我可以发送一些barcodescanner特定的IOCTLs,这将使它回答我?)。
目前我将要使用在这个问题中提出的解决scheme: 使用USB条形码扫描仪读取条形码,同时忽略键盘数据input,而扫描仪产品ID和供应商ID未知
另外我也看到了商业图书馆(这是一个没有任何资源和关于它如何实现的信息,但是考虑到他们在他们的更新日志中有一些词汇“性能计数器”,我想他们在上面的链接中使用了解决scheme)实现这个function,但在x64系统中不起作用。 可能是因为代码混乱,或者因为它可能使用某种filter(迷你)驱动程序。 这是encryption的,我不能重新分配它。
我确切的问题是:有什么办法来确定这个HID键盘实际上不是一个键盘,而是一个条形码扫描仪? 我已经在Win 7 x64上看到它连接成条形码扫描器,而不是键盘(这是一个系统错误,或者某种)。
到底我在做什么:
我现在要做的是:
我在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条形码扫描仪有UsagePage 0x8C和用法0x02。 不幸的是我没有把它作为RAWINPUTDEVICE.dwUsage和RAWINPUTDEVICE.dwUsagePage使用。 可能是因为系统安装它的USB键盘驱动程序的顶部和用户模式,它是真正的USB键盘无法区分。 这些值可能在kernelmode环境中可用(其中一个选项是开发hid筛选器驱动程序)。
这不回答你的具体问题,但无论如何…
一年多以前,我在更不利的情况下实施了条形码阅读器支持。 这是一个报告应用程序与纯Java(跨平台丰富的客户端,主要在Windows)后勤数据关联。 我发现你所说的键盘驱动程序是一样的,至少在第一眼看来,它可以防止在用户模式下区分实际的USB设备。 有更昂贵的设备与自己的驱动程序和高级功能,这将允许某种区别。 我在该环境中遇到的所有条形码阅读器都可以看作是键盘,用于填写SAP表单字段并按下回车键,这是一种常见的情况。 终止可以使用“魔术条形码”或其他制造商特定的方法进行配置。
所以这个决定是针对任何基于JNI的,特定于平台的实现。 相反,我还通过使用以下标准评估某些Swing / AWT表单中的通用键盘输入来实现一种类似截取的方法(扩展版本):
输入被缓冲区消耗,直到机器生成的输入标准不符合,或验证通过,条形码监听器将被通知。 在任何一种情况下,输入都可以像没有其他事情一样被转发。
这已被证明是非常准确的,因为对于人来说,以条码阅读器的速率输入有效序列(几乎)为零的抖动几乎是不可能的。
编辑:
只要挖出Java源代码; 我可以给你一个早期版本的实现代码作为例子(没有保证,也考虑实现CR):
import java.awt.event.KeyEvent; import java.awt.event.Keylistner; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * A {@link Keylistner} 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 AbstractBarcodeInputlistner implements Keylistner { 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 AbstractBarcodeInputlistner(long maxTimeDelta, long maxTimeJitter) { this.input = new String(); this.minPause = AbstractBarcodeInputlistner.DEFAULT_MIN_PAUSE; this.maxTimeDelta = maxTimeDelta; this.maxTimeJitter = maxTimeJitter; this.firstTime = 0; this.firstTimeDelta = 0; this.lastTimeDelta = 0; this.lastTime = 0; } public AbstractBarcodeInputlistner() { this(AbstractBarcodeInputlistner.DEFAULT_MAX_TIME_DELTA, AbstractBarcodeInputlistner.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); }