Java在后台获取USB⼆维码扫描扫描的内容
项⽬需要在Web项⽬中获取扫描扫描的内容,项⽬是Java Web项⽬,最后部署在Linux系统中的。
拿到扫描后,连接在⾃⼰的Windows系统上试了下,插上后,不需要装任何驱动,只要有个⽂本框,就能将扫描到的内容输⼊到⽂本框⾥。反复测试后发现,当前窗⼝的焦点在哪⾥,扫描到的内容就显⽰在哪⾥。
那么现在遇到⼀个问题,项⽬以后要跑起来,是没有任何窗⼝的,是运⾏在后台的,那怎么拿到扫描输⼊的内容呢?
先按照盒⼦上的⼚家名称到官⽹,在官⽹上查到了技术⽀持电话,结果⼈家说⼈家也不知道,他只是硬件层⾯的技术⽀持,如何⽤编程语⾔拿到扫描到的东西,他不清楚。但是他说他们还有串⼝类型的扫描,可⽀持软件编程。挂完电话看了下我们的⼯控机,是没有串⼝的,只有USB接⼝。但是⽹上搜了⼀下,有⽤Java扫描系统的串⼝,然后根据串⼝号获得串⼝输⼊进来的东西,应该不难。既然我们没有串⼝,我也没深⼊研究。
然后就以Java 扫描为关键字搜索相关资料,以前还真有⼈做过这个,在开源中国到⼀个前辈做的项⽬,是⼀个条形码扫描,⼈家实现了。代码那过来研究了⼀番,⼤致明⽩了。
扫描仪其实说⽩了对电脑来说就是个键盘,扫描将扫描得到的内容解析,然后模拟键盘,⼀个⼀个敲⼊到电脑中,最后按⼀下回车键!怪不得焦点在哪个窗⼝就输⼊到哪个窗⼝呢。
那就⼜遇到⼀个问题,Java代码运⾏在Jvm虚拟机内,扫描或键盘输⼊的东西,只有操作系统知道,Jvm虚拟机如何知道呢?那就是JNI编程,通过写C/C++代码,监听操作系统的的输⼊流,然后通过JNI调⽤。虽然我不会JNI,也不会C/C++,
但幸运的是,SUN公司已经实现了这个代码,弄出⼀个叫JNA的东西(Java Native Access),给Java提供了访问操作系统键盘⿏标的能⼒。
然后将⼈家的代码完整拷贝,想跑⼀下,结果没jar包,⼀直报错,根据包名百度,在maven仓库中相关jar包,(想官⽅的jar包和⼀些⽂档,⽆奈,因为被收购的原因,有些链接已经挂了,不到哇)到⼏个,放进去,编译不报错了,运⾏⼀直报错,换了好⼏个jar包,还是不⾏,真是可郁闷了。
最后在⼀个国内的仓库⽹站到⼀个清晰的分类,下载⾥⾯的⼤分类下⾯的⼀组jar包,运⾏成功了。⽹址是www.mvnjar。
运⾏的时候,⼈家的是条形码扫描,只有0到9是个数字,我们的是⼆维码扫描,输⼊的⽂字中有字母数字符合,不够⽤啊,只能⾃⼰再开发了。
当⾃⼰要实现字母键的时候,才发现,字母不是那么好实现的,因为有⼤⼩写区分,还有!@#$%^这些字符需要按住shift键输⼊。JNA提供的钩⼦函数,我们能拿到的只有键盘的键控代码,顿时觉得头⼤了。
分析键控代码值发现,监控代码值跟ASCII代码值中的字母键完全匹配,数字键差⼀些,字符只有极个别有点规律,于是⾃⼰按照键控代码和ASCII码对照了⼀遍,完整实现了所有字母和数字和字符的输⼊。
注意,因为⼆维码扫描只能输⼊⼤⼩写字母、数字、特殊字符,所以其他的键我没管,类似于Ctrl、FN、Alt、F快捷键等。
下⾯贴上代码,感觉只是为了实现功能,代码写得很Low。
import java.util.ArrayList;
import java.util.List;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef.HMODULE;
import com.sun.jna.platform.win32.WinDef.LPARAM;
import com.sun.jna.platform.win32.WinDef.LRESULT;
import com.sun.jna.platform.win32.WinDef.WPARAM;
import com.sun.jna.platform.win32.WinUser;
import com.sun.jna.platform.win32.WinUser.HHOOK;
import com.sun.jna.platform.win32.WinUser.KBDLLHOOKSTRUCT;
import com.sun.jna.platform.win32.WinUser.LowLevelKeyboardProc;
import com.sun.jna.platform.win32.WinUser.MSG;
public class WindowsKeybordListener {
private static HHOOK hhk;
private static LowLevelKeyboardProc keyboardHook;
static List<Character> singleInput = new ArrayList<Character>();
private static String caseCode() {
StringBuffer buffer = new StringBuffer();
for (Character i : singleInput) {
buffer.append(i);
}
String();
}
public static void main(String[] args) {
final User32 lib = User32.INSTANCE;
HMODULE hMod = Kernel32.INSTANCE.GetModuleHandle(null);
keyboardHook = new LowLevelKeyboardProc() {
boolean isShiftUp = false;
@Override
public LRESULT callback(int nCode, WPARAM wParam, KBDLLHOOKSTRUCT info) {
if (nCode >= 0) {
switch (wParam.intValue()) {
case WinUser.WM_KEYDOWN:// 只监听键盘按下
//按下回车键,⽣成完整的字符串,并清空list
if(info.vkCode==13) {
String text = caseCode();
System.out.println(text);
singleInput.clear();
break;
}
//按下的是shift键时,标记⼀下
if (info.vkCode == 160) {
isShiftUp = true;
}
if (!isShiftUp) {
if (info.vkCode >= 65 && info.vkCode <= 90) {//字母键
singleInput.add((char) (info.vkCode + 32));
} else if (info.vkCode >= 219 && info.vkCode <= 221) {//[\]
singleInput.add((char) (info.vkCode - 128));
} else if (info.vkCode >= 188 && info.vkCode <= 191) {//,-./
singleInput.add((char) (info.vkCode - 144));
} else if (info.vkCode >= 48 && info.vkCode <= 57) {//数字键
singleInput.add((char) info.vkCode);
}
if (info.vkCode == 186) {
singleInput.add(';');
}
if (info.vkCode == 187) {
singleInput.add('=');
}
if (info.vkCode == 192) {
singleInput.add('`');
}
if (info.vkCode == 222) {
singleInput.add('\'');
}
} else {
//⼤写字母
if (info.vkCode >= 65 && info.vkCode <= 90) {
singleInput.add((char) info.vkCode );
}
switch (info.vkCode) {
case 186:
singleInput.add(':');
break;
case 187:
singleInput.add('+');
break;
case 188:
singleInput.add('<');
break;
case 189:
singleInput.add('_');
break;
case 190:
singleInput.add('>');
break;
case 191:
singleInput.add('?');
break;
case 192:
singleInput.add('~');
break;
case 219:
singleInput.add('{');
break;
case 220:
singleInput.add('|');
break;
case 221:
singleInput.add('}');
break;
case 222:
singleInput.add('\"');
break;
case 48:
singleInput.add('!');
break;
case 49:
singleInput.add('@');
break;
case 50:
singleInput.add('#');
break;
case 51:
singleInput.add('$');
break;
case 52:
singleInput.add('%');
break;
case 53:
singleInput.add('^');
break;
case 54:
singleInput.add('&');
break;
case 55:
singleInput.add('*');
break;
case 56:
singleInput.add('(');
break;
case 57:
singleInput.add(')');
break;
}
}
break;
case WinUser.WM_KEYUP:// 按键起来
if (info.vkCode == 160) {
isShiftUp = false;
}
break;
}
}
Pointer ptr = Pointer();
long peer = Pointer.nativeValue(ptr);
return lib.CallNextHookEx(hhk, nCode, wParam, new LPARAM(peer));
}
};hhk=lib.SetWindowsHookEx(WinUser.WH_KEYBOARD_LL,keyboardHook,hMod,0);
// This bit never returns from GetMessage
int result;
MSG msg = new MSG();while((result=lib.GetMessage(msg,null,0,0))!=0)
{
if (result == -1) {
// println("error in get message");
break;
} else {
// println("got message");421事件内容
lib.TranslateMessage(msg);
lib.DispatchMessage(msg);
}
}lib.UnhookWindowsHookEx(hhk);
}
}
完整实现后,想在Linux下运⾏,需要改代码,缺发现jar包中的Unix下⾯没有像Windows⼀样有很多类,想官⽅⽂档也不到,苦逼啊,按理来说有Unix和Mac相关的类,应该是全平台⽀持的,就是不知道咋调⽤。
⼜在⽹上各种搜,最后整理了⼀下,有如下结论:
有个项⽬,这个项⽬⽐较简单,只能注册热键,⽐如设定⼀个Ctrl+S可以完成的功能,项⽬运⾏在后台,前台按这个组合键也可以执⾏⾃⼰设定的功能,这个项⽬只能在Windows上运⾏。
然后,有外国⼈,从上⾯那个项⽬中过得灵感,开发了Linux下的⼀个项⽬,能注册⼀些热键在X11窗⼝中,也是使⽤C++的钩⼦函数,通过JNI调⽤。
不过上⾯两个项⽬都是只为注册快捷键使⽤的,不适合⽤于我们这种需要输⼊⼤量⽂字的模块中。
现在还在Linux下合适的,等实现了再开⼀篇帖⼦写⼀下。