QQ泡沫乐园 · 免费提供游戏辅助,破解软件,活动资讯,喜欢记得收藏哦!
综合软件_线报活动_游戏辅助_最新电影_最优质的的辅助分享平台

java调用dll动态库的方法,总的有三种:JNI、JNA、JNative

网络 2022-12-20 13:01

JNA二次开发华视身份证阅读器

前言

这三天了解了一下java调用dll动态库的方式,总的有三种:JNI、JNA、JNative,其中JNA调用DLL是最方便的。本次总结代码可以直接拿去用,不需要做太多的更改。

·JNI

JNI是 Java native interface的简写,它提供了一定的API,实现了Java和其他语言之间的通讯。从Java1.1以后JNI标准成为Java平台的一部分,它容许Java与其他语音进行通讯,但是过程有点冗长和复杂,不利于平常的敏捷开发。

·JNA

JNA是提供一些Java工具类用于在运行期间动态访问系统本地库(win dll),而不需要自己写JNative和JNI代码,只须要在一个插口实现目标native library,在运行以后 JNA会自己去映射相应的方式。

·JNative

JNative是一种才能使用Java语言调用DLL的技术,对JNI进行了封装。

Java使用 JNI来调用dll动态库的调用,工作量略大,一般情况下开发人员会选用JNA或JNative。

使用JNative调用DLL不仅要引入jar包外还须要额外引入一个dll文件,而JNA只须要引入jar即可使用。

添加依赖

<dependency>
        <groupId>net.java.dev.jna</groupId>
        <artifactId>jna</artifactId>
        <version>3.1.0</version>
 </dependency>

SDK资料

二次开发sdk文件下载地址

编写代码

使用springBoot实现本次开发。

termb.dll API函数的动态连接库

sdtapi.dll 安全模块通讯函数

WltRs.dll 身份证照片解码库

加载dll,将dll文件置于resources文件夹下,网上有很多关于Dll放置位置的文章,本次采用的是置于项目中,方便读取DLL

关于获取DLL路径的方式,可以参考

JNA实现library插口


```java
public interface TermbDLL extends Library {
    // termb.dll API函数的动态联接库
    // sdtapi.dll 安全模块通讯函数
    // WltRs.dll 身份证相片解码库
    TermbDLL termbDLL = (TermbDLL) Native.loadLibrary(TermbDLL.class.getResource("/Termb.dll").getPath().substring(1), TermbDLL.class);
    /**
     * 连接读卡器
     */
    int CVR_InitComm(int Port);
    /**
     * 卡认证
     */
    int CVR_Authenticate();
    /**
     * 读卡
     */
    int CVR_Read_Content(int active);

    /**
     * 读卡操作 含指纹
     */
    int CVR_Read_FPContent(int active);
    /**
     * 关闭连接
     */
    int CVR_CloseComm();
    /**
     * 找卡
     */
    int CVR_FindCard();
    /**
     * 选卡
     */
    int CVR_SelectCard();
    /**
     * 获取姓名
     */
    int GetPeopleName(byte[] name, IntByReference len);
    /**
     * 得到性别信息
     */
    int GetPeopleSex(byte[] sex, IntByReference len);
    /**
     * 得到民族信息
     */
    int GetPeopleNation(byte[] strTmp, IntByReference strLen);
    /**
     * 得到出生日期
     */
    int GetPeopleBirthday(byte[] strTmp, IntByReference strLen);
    /**
     * 得到地址信息
     */
    int GetPeopleAddress(byte[] strTmp, IntByReference strLen);
    /**
     * 得到身份证号信息
     *
     */
    int GetPeopleIDCode(byte[] strTmp, IntByReference strLen);
    /**

     * 得到发证机关信息
     */
    int GetDepartment(byte[] strTmp, IntByReference strLen);
    /**
     * 得到有效开始日期
     */
    int GetStartDate(byte[] strTmp, IntByReference strLen);
    /**
     * 得到有效截止日期
     */
    int GetEndDate(byte[] strTmp, IntByReference strLen);
    /**
     * 得到安全模块号
     */
    int CVR_GetSAMID(byte[] strTmp);
    /**
     * 得到指纹数据,不超过1024字节
     */
    int GetFPDate(byte[] strTmp, IntByReference strLen);
    /**
     * 得到头像照片bmp数据,不超过38862字节
     */
    int GetBMPData(byte[] strTmp, IntByReference strLen);
    /**
     * 得到头像照片base64编码数据,不超过38862*2字节
     */
    int Getbase64BMPData(byte[] strTmp, IntByReference strLen);
    /**
     * 得到头像照片jpg数据,不超过38862字节
     */
    int Getbase64JpgData(byte[] strTmp, IntByReference strLen);
}

``

具体调用的方式


@Service
public class CPReadIdCardInfoService {
    //成功状态码
    private static final int SUCCESS = 1;
    //失败状态码
    private static final int FAIL = 0;

    private static final String CHARACTER_ENCODING = "gb2312";
    public CPReadIdCardInfoModel readIdCardInfo() throws Exception {
        TermbDLL instance = buildInstance();
        try {
            connAndCheckDevice(instance);
            //读取身份证各信息
            IntByReference len = new IntByReference();
            //1、姓名
            String name = getPeopleName(instance, len);
            //2、性别
            String sex = getPeopleSex(instance,len);
          
           return readIdCardInfoModel;
        } finally {
            instance.CVR_CloseComm();
        }
    }
    private String getPeopleName(TermbDLL instance, IntByReference len) {
        try {
        	//长度更具华视文档设定
            byte [] nameStrTmp = new byte[30];
			//JNA调用DLL方法
            instance.GetPeopleName(nameStrTmp,len);
            return handleResult(nameStrTmp);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    private String getPeopleSex(TermbDLL instance, IntByReference len) {
        try {
            byte [] sexStrTmp = new byte[2];
            instance.GetPeopleSex(sexStrTmp,len);
            return handleResult(sexStrTmp);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    private String getPeopleInfo(TermbDLL instance, String methodName, IntByReference len) {
        try {

            byte [] strTmp = new byte[10];
            Method method = instance.getClass().getMethod(methodName, byte[].class, IntByReference.class);
            method.invoke(instance,strTmp,len);
            return handleResult(strTmp);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    private String handleResult(byte[] nameByte) throws UnsupportedEncodingException {
		//处理返回值防止乱码
        return new String(nameByte, CHARACTER_ENCODING).trim();
    }
    private void connAndCheckDevice(TermbDLL instance) throws Exception {
        //1、初始化连接
        cvrInitComm(instance);
        //2、读卡(同一个身份证,只要读取一次,下次读取优先从缓存中读取,并且CVR_Authenticate返回为2,卡认证失败,所以优先读卡一次)
        int readStatus = instance.CVR_Read_Content(1); //读取卡
        //2、卡认证,超出两分钟就是本次读卡失败
        int authenticate = getAuthenticateStatus(instance, readStatus);
        if (authenticate != SUCCESS)
            throw new Exception("读卡失败,请重新放置卡片");
        if (readStatus != SUCCESS)
            readStatus = instance.CVR_Read_Content(1); //读取卡
        if (readStatus != SUCCESS)
            throw new Exception("证件信息读取失败,请重新放置证件");
    }
    /**
     * 卡认证
     * @param instance     DLL
     * @param readStatus   同一个身份证,只要读取一次,下次读取优先从缓存中读取,并且CVR_Authenticate返回为2,卡认证失败,所以优先读卡一次
     */
    private int getAuthenticateStatus(TermbDLL instance, int readStatus) throws InterruptedException {
        if (readStatus == SUCCESS)
            return SUCCESS;

        final long deadline = System.nanoTime() + TimeUnit.MINUTES.toNanos(2L);
        int authenticate = FAIL;
        //循环读卡,失效时间为2分钟
        while (authenticate != SUCCESS){
            authenticate = instance.CVR_Authenticate();
            if (deadline ==SUCCESS)continue;
            if (deadline - System.nanoTime()<0L)break;
            Thread.sleep(100);//短暂休眠
        }
        return authenticate;
    }
    private void cvrInitComm(TermbDLL instance) throws Exception {
        int cvrInitComm = instance.CVR_InitComm(1001);
        if (cvrInitComm != SUCCESS){
            throw new Exception("读卡器连接失败,请检查设备状态");
        }
    }
    private TermbDLL buildInstance() throws Exception {
        URL dllResource = TermbDLL.class.getResource("/Termb.dll");
        if(dllResource == null) { throw new Exception("请检查设备状态"); }
        String dllPath = dllResource.getPath();
        if (StringUtils.isBlank(dllPath)) { throw new Exception("请检查设备状态"); }
        if (StringUtils.startsWithIgnoreCase(dllPath,"/")){
            dllPath = dllPath.substring(1);
        }
        try {
            return (TermbDLL) Native.loadLibrary(dllPath, TermbDLL.class);
        } catch (Exception e) {
            throw new Exception("请检查设备状态");
        }
    }
}

遇到的问题(坑)

问题一:华视的设备驱动,在windows笔记本关掉以后,偶尔会出现须要重新安装;

问题二:同时多次发起申请,会导致设备阻塞

问题三:DDL路径问题