用JNA开发身份证阅读程序

阅读更多

JNA(Java Native Access)是建立在JNI基础上的开源Java框架。 
    项目网址: 
https://github.com/twall/jna 
    使用JNI调用dll是比较麻烦的,如已有一个dll文件,还需要使用C语言另外编写一个dll(根据由java代码生成的C/C++ 头文件编写 ),使用者需要比较了解C/C++。

    使用JNA, 需再编写适配用的dll,只 编写一个 Java 接口和一些代码,作为dll的代理,就可在Java程序中调用dll,JNA自动实现Java和C的数据类型映射。

准备工作

     下载JAR包  JNA当前版本为 3.5.1,包含两个jar包:jna.jar、platform.jar,一般使用只需jna.jar就可以了,platform.jar提供了许多工具类。

     JNA运行时会到path环境变量指定的路径下寻找dll库,用户也可以设置java 系统参数-Djna.library.path=...

 

JNA官网的一个简单例子

     使用C运行时库msvcrt.dll中的printf函数打印字符,

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;

/** Simple example of JNA interface mapping and usage. */
public class HelloWorld {

    // This is the standard, stable way of mapping, which supports extensive
    // customization and mapping of Java to native types.
    public interface CLibrary extends Library {

CLibrary INSTANCE = (CLibrary)Native.loadLibrary((Platform.isWindows() ? "msvcrt" : "c"),
CLibrary.class);

void printf(String format, Object... args);

    }

   public static void main(String[] args) {
     CLibrary.INSTANCE.printf("Hello, World\n");
     for (int i=0;i < args.length;i++) {
       CLibrary.INSTANCE.printf("Argument %d: %s\n", i, args[i]);
     }
   }
}

 

使用JNA开发身份证阅读器

     用到的身份证阅读器基本类库:sdtapi.dll、WltRs.dll。以下为示例代码,其中列出了sdtapi.dll、WltRs.dll中提供的方法(方法名可以通过vc dumpbin等工具查看),介绍了阅读身份证的基本流程及接口的使用方法。

---IDCardReader---

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.util.HashMap;

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.ptr.IntByReference;

public class IDCardReader {

/**
* sdtapi接口
* 
* 声明sdtapi.dll中的导出方法
* 
* 端口类api: SDT_OpenPort,SDT_ClosePort,SDT_GetCOMBaud,SDT_SetCOMBaud
* SAM(身份证安全控制模块security account manager)类aip: SDT_GetSAMStatus,SDT_GetSAMID,SDT_GetSAMIDToStr,SDT_ResetSAM
* 身份证卡类api: SDT_StartFindIDCard,SDT_SelectIDCard,SDT_ReadBaseMsg,SDT_ReadBaseMsgToFile,SDT_ReadNewAppMsg
* 
* 在普通开发中仅需使用部分端口类、身份证卡类api即可满足需要
* 
* @author sunjc
*
*/
public interface Sdtapi extends Library {
Sdtapi INSTANCE = (Sdtapi) Native.loadLibrary("sdtapi", Sdtapi.class);

/************************端口类API *************************/

/**
* 打开串口/USB口
* 
* @param iPort 端口号
* 1-16(十进制)为串口,1001-1016(十进制)为USB口
* @return
*/
int SDT_OpenPort(int iPort);

/**
* 关闭串口/USB口
* 
* @param iPort 端口号
* @return
*/
int SDT_ClosePort(int iPort);

/**
* 查看串口波特率
* 
* @param iPort 串口号 (1-16)
* @param pucBaudRate 串口波特率
* @return
*/
int SDT_GetCOMBaud(int iPort, IntByReference pucBaudRate);

/**
* 设置串口波特率
* 
* @param iPort 串口号 (1-16)
* @param puiCurrBaud 当前串口波特率
* @param puiSetBaud 要设置的串口波特率
* 波特率只能为以下项115200,57600,38400,19200,9600
* @return
*/
int SDT_SetCOMBaud(int iPort, int puiCurrBaud, int puiSetBaud);

/************************SAM类API *************************/

/**
* 对SAM_V 进行状态检测
* 
* @param iPort 端口号
* @param iIfOpen
* @return
*/
int SDT_GetSAMStatus(int iPort, int iIfOpen);

/**
* 读取SAM_V 的编号(十六进制)
* 
* @param iPort 端口号
* @param pucSAMID SAM_V编号
* @param iIfOpen
* @return
*/
int SDT_GetSAMID(int iPort, CharBuffer pucSAMID, int iIfOpen);

/**
* 读取SAM_V 的编号(字符串格式)
* 
* @param iPort 端口号
* @param pucSAMID SAM_V编号
* @param iIfOpen
* @return
*/
int SDT_GetSAMIDToStr(int iPort, CharBuffer pucSAMID, int iIfOpen);

/**
* 对SAM_V 复位
* 
* @param iPort 端口号
* @param iIfOpen
* @return
*/
int SDT_ResetSAM(int iPort, int iIfOpen);

/************************身份证卡类API *************************/

/**
* 找卡
* 
* @param iPort 端口号
* @param pucManaInfo 证/卡芯片治理号(4字节)
* @param iIfOpen
* 0表示不在该函数内部打开和关闭串口,此时确保之前调用了SDT_OpenPort来打开端口,并且在不需要与端口通信时,调用SDT_ClosePort关闭端口;
* 非0表示在API函数内部包含了打开端口和关闭端口函数,之前不需要调用SDT_OpenPort,也不用再调用SDT_ClosePort
* @return
*/
int SDT_StartFindIDCard(int iPort, IntByReference pucManaInfo, int iIfOpen);

/**
* 选卡
* 
* @param iPort 端口号
* @param pucManaMsg 证/卡芯片序列号(8字节)
* @param iIfOpen
* @return
*/
int SDT_SelectIDCard(int iPort, IntByReference pucManaMsg, int iIfOpen);

/**
* 读取固定信息
* 
* @param iPort 端口号
* @param pucCHMsg 文字信息
* @param puiCHMsgLen
* @param pucPHMsg 照片信息
* @param puiPHMsgLen
* @param iIfOpen
* @return
*/
int SDT_ReadBaseMsg(int iPort, CharBuffer pucCHMsg,
IntByReference puiCHMsgLen, ByteBuffer pucPHMsg,
IntByReference puiPHMsgLen, int iIfOpen);

/**
* 读取固定信息,将信息写到文件
* 
* @param iPort 端口号
* @param chMsgFile 文本信息文件名
* @param puiCHMsgLen
* @param wltFile 照片信息文件名(扩展名为wlt)
* @param puiPHMsgLen
* @param iIfOpen
* @return
*/
int SDT_ReadBaseMsgToFile(int iPort, String chMsgFile,
IntByReference puiCHMsgLen, String wltFile,
IntByReference puiPHMsgLen, int iIfOpen);

/**
* 读取追加信息
* 
* @param iPort 端口号
* @param pucAppMsg 追加信息
* @param puiAppMsgLen
* @param iIfOpen
* @return
*/
int SDT_ReadNewAppMsg(int iPort, CharBuffer pucAppMsg,
IntByReference puiAppMsgLen, int iIfOpen);

/**
* 设置射频适配器最大通信字节数
* 
* @param iPort
* @param ucByte
* @param iIfOpen
* @return
*/
int SDT_SetMaxRFByte(int iPort, String ucByte, int iIfOpen);
}

public interface Wltrs extends Library {
Wltrs INSTANCE = (Wltrs) Native.loadLibrary("WltRs", Wltrs.class);

/**
* 将wlt文件解码成bmp文件
* 
* @param wltFile wlt文件名
* @param intf 阅读设备通讯接口类型(1—RS-232C,2—USB)
* @return 
* 1 相片解码解码正确 
* 0 调用sdtapi.dll错误 
* -1 相片解码错误 
* -2 wlt文件后缀错误 
* -3 wlt文件打开错误 
* -4 wlt文件格式错误 
* -5 软件未授权 
* -6 设备连接错误
* 
* wlt文件的扩展名要固定为”.wlt”,如:xp.wlt,相片解码成xp.bmp
* 本方法要与sdtapi.dll关联使用,并确认通讯端口处于关闭状态;
*/
int GetBmp(String wltFile, int intf);
}

private static IDCardReader instance;

private int port;

private IDCardReader() {

}

public static IDCardReader getInstance() {
if (instance == null) {
instance = new IDCardReader();
}

return instance;
}

/**
* 以指定波特率打开串口
* @param port
* @param baudRate
* @return
*/
public boolean openPort(int port, int baudRate) {
int resultCode = -1;

// 取得串口当前波特率
IntByReference currBaudRate = new IntByReference();
resultCode = Sdtapi.INSTANCE.SDT_GetCOMBaud(port, currBaudRate);

if (resultCode == 144) {
// 设置串口波特率,如设置不成功将使用原波特率
resultCode = Sdtapi.INSTANCE.SDT_SetCOMBaud(port, currBaudRate.getValue(), baudRate);
}

// 打开串口
resultCode = Sdtapi.INSTANCE.SDT_OpenPort(port);

if (resultCode != 144) {
return false;
}

this.port = port;
return true;
}

/**
* 读卡
* @return
*/
public IDCardInfo readCard() {
int rtn = -1;

// 找卡
rtn = Sdtapi.INSTANCE.SDT_StartFindIDCard(port, new IntByReference(), 0);

if (rtn != 159) {
return null;
}

// 选卡
rtn = Sdtapi.INSTANCE.SDT_SelectIDCard(port, new IntByReference(), 0);

if (rtn != 144) {
return null;
}

// 读卡
CharBuffer cHMsg = CharBuffer.allocate(256); // 文字信息
ByteBuffer pHMsg = ByteBuffer.allocate(1024); // 照片信息
rtn = Sdtapi.INSTANCE.SDT_ReadBaseMsg(port, cHMsg, new IntByReference(), pHMsg, new IntByReference(), 0);

IDCardInfo idCardInfo = new IDCardInfo();
parseCHMsg(cHMsg.toString(), idCardInfo);
return idCardInfo;
}

/**
* 关闭端口
*/
public void closePort() {
Sdtapi.INSTANCE.SDT_ClosePort(port);
}

public static void main(String[] args) throws InterruptedException {
// 打开端口
IDCardReader idCardReader = IDCardReader.getInstance();
idCardReader.openPort(4, 115200); // 默认波特率为115200,一般不需要修改,速度最快

// 读卡
long starttime = System.currentTimeMillis();
System.out.println(idCardReader.readCard());
long endtime = System.currentTimeMillis(); 
System.out.println("time:" + (endtime - starttime));

// 关闭端口
idCardReader.closePort();
}

public static void test() {
// 取串口波特率
IntByReference pucBaudRate = new IntByReference(); 
System.out.println("GetCOMBaud:"+ Sdtapi.INSTANCE.SDT_GetCOMBaud(4, pucBaudRate));
System.out.println("pucBaudRate:" + pucBaudRate.getValue());

// 设置串口波特率
System.out.println("SetCOMBaud:" + Sdtapi.INSTANCE.SDT_SetCOMBaud(4, pucBaudRate.getValue(), 9600));

// 打开端口
System.out.println("OpenPort:" + Sdtapi.INSTANCE.SDT_OpenPort(4));

// 找卡
IntByReference pucManaInfo = new IntByReference();
System.out.println("StartFindIDCard:" + Sdtapi.INSTANCE.SDT_StartFindIDCard(4, pucManaInfo, 0));
System.out.println("pucManaInfo:" + pucManaInfo.getValue());

// 选卡
IntByReference pucManaMsg = new IntByReference();
System.out.println("SelectIDCard:" + Sdtapi.INSTANCE.SDT_SelectIDCard(4, pucManaMsg, 0));
System.out.println("pucManaMsg:" + pucManaMsg.getValue());

// 读卡
CharBuffer pucCHMsg = CharBuffer.allocate(256);
ByteBuffer pucPHMsg = ByteBuffer.allocate(1024);
IntByReference puiCHMsgLen = new IntByReference();
IntByReference puiPHMsgLen = new IntByReference();
System.out.println("ReadBaseMsg:" + Sdtapi.INSTANCE.SDT_ReadBaseMsg(4, pucCHMsg, puiCHMsgLen,
pucPHMsg, puiPHMsgLen, 0));
System.out.println("pucCHMsg:" + pucCHMsg + ",puiCHMsgLen:" + puiCHMsgLen.getValue());
System.out.println("身份证号:" + pucCHMsg.toString().substring(61,79)); 
System.out.println("pucPHMsg:" + pucPHMsg + ",puiPHMsgLen:" + puiPHMsgLen.getValue());

// 读卡并写文件
// System.out.println("ReadBaseMsgToFile:" + Sdtapi.INSTANCE.SDT_ReadBaseMsgToFile(4, "xx.txt", puiCHMsgLen,
// "xx.wlt", puiPHMsgLen, 1));

// 生成照片文件
writeFile("xx.wlt", pucPHMsg.array());
System.out.println("GetBmp:" + Wltrs.INSTANCE.GetBmp("xx.wlt", 2));

// 读取追加信息
// CharBuffer pucAppMsg = CharBuffer.allocate(40);
// IntByReference puiAppMsgLen = new IntByReference();
// System.out.println("ReadNewAppMsg:"+ Sdtapi.INSTANCE.SDT_ReadNewAppMsg(4, pucAppMsg, puiAppMsgLen, 1));
// System.out.println("pucAppMsg:" + pucAppMsg + ",puiAppMsgLen:" + puiAppMsgLen.getValue());

// 关闭端口
Sdtapi.INSTANCE.SDT_ClosePort(4);

}

private static void writeFile(String filename,byte[] content) {
File wltFile = new File(filename);
try {
FileOutputStream fo = new FileOutputStream(wltFile);
fo.write(content);
fo.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

/**
* 解析文字信息
* @param chMsg
* @param idCardInfo
*/
private void parseCHMsg(String chMsg, IDCardInfo idCardInfo) {
idCardInfo.setName(chMsg.substring(0, 15).trim());
idCardInfo.setGender(genderCode.get(chMsg.substring(15, 16)));
idCardInfo.setNation(nationCode.get(chMsg.substring(16, 18)));
idCardInfo.setBirthday(chMsg.substring(18, 26));
idCardInfo.setAddress(chMsg.substring(26, 61).trim());
idCardInfo.setCardNo(chMsg.substring(61, 79).trim());
idCardInfo.setGrantDept(chMsg.substring(79, 94).trim());
idCardInfo.setUsefulLifeBegin(chMsg.substring(94, 102));
idCardInfo.setUsefulLifeEnd(chMsg.substring(102, 110));
}

private HashMap nationCode = new HashMap();
{
nationCode.put("01", "汉族");
nationCode.put("02", "蒙古族");
nationCode.put("03", "回族");
nationCode.put("04", "藏族");
nationCode.put("05", "维吾尔族");
nationCode.put("06", "苗族");
nationCode.put("07", "彝族");
nationCode.put("08", "壮族");
nationCode.put("09", "布依族");
nationCode.put("10", "朝鲜族");
nationCode.put("11", "满族");
nationCode.put("12", "侗族");
nationCode.put("13", "瑶族");
nationCode.put("14", "白族");
nationCode.put("15", "土家族");
nationCode.put("16", "哈尼族");
nationCode.put("17", "哈萨克族");
nationCode.put("18", "傣族");
nationCode.put("19", "黎族");
nationCode.put("20", "傈僳族");
nationCode.put("21", "佤族");
nationCode.put("22", "畲族");
nationCode.put("23", "高山族");
nationCode.put("24", "拉祜族");
nationCode.put("25", "水族");
nationCode.put("26", "东乡族");
nationCode.put("27", "纳西族");
nationCode.put("28", "景颇族");
nationCode.put("29", "柯尔克孜族");
nationCode.put("30", "土族");
nationCode.put("31", "达翰尔族");
nationCode.put("32", "仫佬族");
nationCode.put("33", "羌族");
nationCode.put("34", "布朗族");
nationCode.put("35", "撒拉族");
nationCode.put("36", "毛南族");
nationCode.put("37", "仡佬族");
nationCode.put("38", "锡伯族");
nationCode.put("39", "阿昌族");
nationCode.put("40", "普米族");
nationCode.put("41", "塔吉克族");
nationCode.put("42", "怒族");
nationCode.put("43", "乌孜别克族");
nationCode.put("44", "俄罗斯族");
nationCode.put("45", "鄂温克族");
nationCode.put("46", "德昂族");
nationCode.put("47", "保安族");
nationCode.put("48", "裕固族");
nationCode.put("49", "京族");
nationCode.put("50", "塔塔尔族");
nationCode.put("51", "独龙族");
nationCode.put("52", "鄂伦春族");
nationCode.put("53", "赫哲族");
nationCode.put("54", "门巴族");
nationCode.put("55", "珞巴族");
nationCode.put("56", "基诺族");
nationCode.put("57", "其它");
nationCode.put("98", "外国人入籍");
}

private HashMap genderCode = new HashMap();
{
genderCode.put("0", "未知");
genderCode.put("1", "男");
genderCode.put("2", "女");
genderCode.put("9", "未说明");
}
}

/**
* 身份证信息
* 
* @author sunjc
* 
*/
public class IDCardInfo {
private String name;
private String gender;
private String nation;
private String birthday;
private String address;
private String cardNo;
private String grantDept;
private String usefulLifeBegin;
private String usefulLifeEnd;
private String newAddres;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getGender() {
return gender;
}

public void setGender(String gender) {
this.gender = gender;
}

public String getNation() {
return nation;
}

public void setNation(String nation) {
this.nation = nation;
}

public String getBirthday() {
return birthday;
}

public void setBirthday(String birthday) {
this.birthday = birthday;
}

public String getAddress() {
return address;
}

public void setAddress(String address) {
this.address = address;
}

public String getCardNo() {
return cardNo;
}

public void setCardNo(String cardNo) {
this.cardNo = cardNo;
}

public String getGrantDept() {
return grantDept;
}

public void setGrantDept(String grantDept) {
this.grantDept = grantDept;
}

public String getUsefulLifeBegin() {
return usefulLifeBegin;
}

public void setUsefulLifeBegin(String usefulLifeBegin) {
this.usefulLifeBegin = usefulLifeBegin;
}

public String getUsefulLifeEnd() {
return usefulLifeEnd;
}

public void setUsefulLifeEnd(String usefulLifeEnd) {
this.usefulLifeEnd = usefulLifeEnd;
}

public String getNewAddres() {
return newAddres;
}

public void setNewAddres(String newAddres) {
this.newAddres = newAddres;
}

@Override
public String toString() {
String cardInfo = name + "|" + gender + "|" + nation + "|" + birthday
+ "|" + address + "|" + cardNo + "|" + grantDept + "|"
+ usefulLifeBegin + "|" + usefulLifeEnd + "|" + newAddres;
return cardInfo;
}
}

 

你可能感兴趣的:(jna)