前述: 公司是做社保,醫療行業的,接了個單子,制作生存認證項目,使用曙光易通的指靜脈儀,然后誕生了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,無法確定最佳手指位置,最后作為了認證失敗處理