前述: 公司是做社保,医疗行业的,接了个单子,制作生存认证项目,使用曙光易通的指静脉仪,然后诞生了n多需求,在前期已完成electron的ffi模块来调用dll,现把这两周遇到的坑记录如下
需求1:指静脉过程中不要弹框
需求2:兼容知能易通的指静脉仪(注:知能易通是曙光易通的旧版本,现早不提供)
需求1的解决:
先请求厂家帮助提供无弹框的dll,结果又几率仍然弹框,所以换成我用java来写
需求如下:1.程序检测窗口出现拍摄登录图像等字样,发送回车命令 2.随客户端启动
使用的jna框架调用user32命令,在执行中可以加入循环,判断electron标题,如果标题不存在,即关闭,结束循环,关闭程序
package yinhai.com; import com.sun.jna.Native; public interface User32 extends W32API { User32 INSTANCE = (User32) Native.loadLibrary("user32", User32.class, DEFAULT_OPTIONS); boolean ShowWindow(HWND hWnd, int nCmdShow); boolean SetForegroundWindow(HWND hWnd); HWND FindWindow(String winClass, String title); HWND FindWindow(int winClass, String title); HWND FindWindowEx(HWND hWnd, HWND childWnd, int wParam, int lParam); HWND FindWindowEx(HWND hWnd, int childWnd, int wParam, int lParam); boolean PostMessage(HWND hWnd, Integer Msg, Integer wParam, Integer lParam); boolean PostMessage(HWND hWnd, int Msg, int wParam, int lParam); boolean PostMessage(HWND hWnd, String Msg, int wParam, int lParam); boolean PostMessage(HWND hWnd, String Msg, String wParam, String lParam); boolean PostMessage(HWND hWnd, int Msg, String wParam, String lParam); boolean PostMessage(HWND hWnd, int Msg, String wParam, int lParam); boolean PostMessage(HWND hWnd, String Msg, String wParam, int lParam); void keybd_event(String bVk, String bScan, String dwFlags, String dwExtralnfo); void keybd_event(int bVk, String bScan, String dwFlags, String dwExtralnfo); void keybd_event(String bVk, int bScan, int dwFlags, int dwExtralnfo); void keybd_event(int bVk, int bScan, int dwFlags, int dwExtralnfo); void keybd_event(int bVk, int bScan, String dwFlags, int dwExtralnfo); void keybd_event(String bVk, int bScan, String dwFlags, int dwExtralnfo); boolean SendMessage(HWND hWnd, Integer Msg, Integer wParam, Integer lParam); boolean SendMessage(HWND hWnd, int Msg, int wParam, int lParam); boolean SendMessage(HWND hWnd, String Msg, int wParam, int lParam); boolean SendMessage(HWND hWnd, String Msg, String wParam, String lParam); boolean SendMessage(HWND hWnd, int Msg, String wParam, String lParam); boolean SendMessage(HWND hWnd, int Msg, String wParam, int lParam); boolean SendMessage(HWND hWnd, String Msg, String wParam, int lParam); }
package yinhai.com; import java.util.HashMap; import java.util.Map; import com.sun.jna.FromNativeContext; import com.sun.jna.Native; import com.sun.jna.Pointer; import com.sun.jna.PointerType; import com.sun.jna.win32.StdCallLibrary; import com.sun.jna.win32.W32APIFunctionMapper; import com.sun.jna.win32.W32APITypeMapper; @SuppressWarnings({ "unchecked", "serial" }) public interface W32API extends StdCallLibrary { Map UNICODE_OPTIONS = new HashMap() { private static final long serialVersionUID = 1L; { put(OPTION_TYPE_MAPPER, W32APITypeMapper.UNICODE); put(OPTION_FUNCTION_MAPPER, W32APIFunctionMapper.UNICODE); } }; Map ASCII_OPTIONS = new HashMap() { { put(OPTION_TYPE_MAPPER, W32APITypeMapper.ASCII); put(OPTION_FUNCTION_MAPPER, W32APIFunctionMapper.ASCII); } }; Map DEFAULT_OPTIONS = Boolean.getBoolean("w32.ascii") ? ASCII_OPTIONS : UNICODE_OPTIONS; public static class HANDLE extends PointerType { public Object fromNative(Object nativeValue, FromNativeContext context) { Object o = super.fromNative(nativeValue, context); if (INVALID_HANDLE_VALUE.equals(o)) return INVALID_HANDLE_VALUE; return o; } } public static class HWND extends HANDLE { } HANDLE INVALID_HANDLE_VALUE = new HANDLE() { { super.setPointer(Pointer.createConstant(-1)); } public void setPointer(Pointer p) { throw new UnsupportedOperationException("Immutable reference"); } }; }
package yinhai.com; import java.io.IOException; import javax.swing.JOptionPane; public class ListenerWindow { public static void main(String[] args) { W32API.HWND targetHwnd=null; W32API.HWND hwnd; W32API.HWND ytjHwnd; W32API.HWND ytjErrorHwnd; W32API.HWND ytjFallHwnd; // 循环监听一体机窗口 boolean ytjState = true; try { Thread.sleep(2000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } while (ytjState) { try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } hwnd = User32.INSTANCE.FindWindow(0, "拍摄登录图像"); ytjHwnd = User32.INSTANCE.FindWindow(0, "生存认证指静脉离线采集程序"); ytjErrorHwnd = User32.INSTANCE.FindWindow(0, "Sugon"); ytjFallHwnd = User32.INSTANCE.FindWindow(0, "信息提示"); // W32API.HWND hwnd = User32.INSTANCE.FindWindow(0, "Sugon"); // W32API.HWND childHwnd = User32.INSTANCE.FindWindowEx(hwnd, 0, 0, // 0); // User32.INSTANCE.SetForegroundWindow(hwnd); /* * for (int i = 0; i < 100; i++) { // keyPress(82); // * //User32.INSTANCE.PostMessage(hwnd, // 82, 0,0); * backKeyPress(childHwnd, 82); } */ // System.out.println("运行中"); if (hwnd != null||ytjErrorHwnd != null||ytjFallHwnd != null) { if(hwnd!=null) { targetHwnd=hwnd; hwnd=null; }else if(ytjErrorHwnd!=null){ targetHwnd=ytjErrorHwnd; ytjErrorHwnd=null; }else if(ytjFallHwnd!=null){ targetHwnd=ytjFallHwnd; ytjFallHwnd=null; } User32.INSTANCE.ShowWindow(targetHwnd, 9); User32.INSTANCE.SetForegroundWindow(targetHwnd); // 发送回车命令 User32.INSTANCE.PostMessage(targetHwnd, 256, 0xD, 0); } /*//关闭 if (ytjHwnd == null) { // System.out.println("no exe"); //JOptionPane.showMessageDialog(null, "辅助采集程序关闭", "指静脉离线程序", JOptionPane.ERROR_MESSAGE); ytjState = false; }*/ /* * if (hwnd != null) { User32.INSTANCE.ShowWindow(hwnd, 9); * User32.INSTANCE.SetForegroundWindow(hwnd); } else { try { * System.out.println(" can't find the window !!"); * Runtime.getRuntime().exec("NotePad.exe"); } catch (IOException e) * { e.printStackTrace(); } } */ } } public static void backKeyPress(W32API.HWND hwnd, int keyNum) { User32.INSTANCE.PostMessage(hwnd, 256, keyNum, 0); } public static void backKeyRelease(W32API.HWND hwnd, int keyNum) { User32.INSTANCE.PostMessage(hwnd, 257, keyNum, 0); } public static void keyPress(int keyNum) { User32.INSTANCE.keybd_event(keyNum, 0, 0, 0); User32.INSTANCE.keybd_event(keyNum, 0, "KEYEVENTF_KEYUP", 0); } }
接着为了扩展项目通用性,把java18的32和64位的jre包提取出来,然后写了一个bat隐藏启动(如下图)
然后在electron的main.js中让在加载中调用bat
//加载cmd命令 var execState=new Boolean(true); const exec = require('child_process'); //在基本命令加载完后再启动cmd加载 app.on('will-finish-launching',() => { if(execState==true){ runExec(execState); // 生效啦,可以做些什么执行一种相对的同步状态,例如判断输出内容到什么了 execState=false; } }); // 任何你期望执行的cmd命令,ls都可以 let cmdStr = "start.bat";//'./你的可执行程序名称 -p 需要输入密码的话' // 执行cmd命令的目录,如果使用cd xx && 上面的命令,这种将会无法正常退出子进程 let cmdPath = process.cwd()+"\\resources\\app\\jar"; // 子进程名称 let workerProcess function runExec() { exec.execFile(cmdStr, [], {cwd:cmdPath}, function(error, stdout, stderr) { console.log(error); console.log(stdout); });
接下来讲讲知能易通和曙光易通指静脉仪的坑
首先要知道知能易通和曙光易通是一个公司,曙光易通是知能易通的升级版
直接说解决方法:
1.找厂家要最新版的驱动,我用的是5.0.0.3NoP-AC,然后必须连上曙光易通的静脉仪,卸载旧驱动,安装新驱动
2.使用CoorperateRegistTool.exe给程序生成reg文件
3.如果电脑是32位,需用管理员身份运行reg.reg,如果电脑是64位,
需用管理员运行 C:\Windows\SysWOW64目录下的cmd.exe,输入 reg import 路径\reg.reg
4.从驱动的安装demo下把dll拷出来,调用dll获取控件版本返回的是-1,但其实是获取了,获得的值与页面调html调ocx控件返回的值不同
5.然后除了获取指纹模板其他方法可以不变,方法如下图
尽管开发完成,但是在使用过程中仍然能明显感到使用的吃力,如1:N认证时返回255,无法确定最佳手指位置,最后作为了认证失败处理