这不能回答你的具体问题,但无论如何......
一年多前,我在更不利的情况下实现了条码阅读器支持。它是用于与纯 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);
}