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 {
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.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.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;
}
}