方案综述
使用Java调用DLL动态链接库的方案通常有:JNI, JNA, Jacob.
- JNI:JNI的应用方案是基于Java类和本地函数相映射的。其使用DLL的步骤还是相对比较麻烦,不但涉及到Java编程,还涉及到C/C++编程。
- JNA:JNA(Java Native Access)框架是一个开源的Java框架,是SUN公司主导开发的,建立在经典的JNI的基础之上的一个框架。
- Jacob:Jacob是Java-Com Bridge的缩写,也可以用来调用DLL。其底层也是使用JNI实现,也具有Windows 的平台依赖性。调用相当方便,不用进行C/C++开发,不用对原始DLL进行封装就可以方便使用。
JNI,JNA,jawin,jacob和Jcom之间的区别是什么,它们的调用效率怎么排名? —— Accelerator的回答 - 知乎
JNA与JNI简单对比
JNA使用比JNI简单,但是JNI比JNA性能更好
JNI vs. JNA performance——stackoverflow
JNA示例:
使用注意:32位JDK只能调用32位dll,64位JDK只能调用64位dll
1.引入JNA相关jar包
image.png2.定义结构体
找到对应C++的头文件
头文件
C++结构体1-1
在头文件中找到使用到的结构体声明
头文件中的结构体1-1
//=====================================
// DeviceID information
//=====================================
typedef struct _DEVICEID01 {
int Size ; //Size
int Version ; //Version
char MFG[256] ; //Manufacturer name
char CMD[256] ; //Support command type
char MDL[256] ; //Product name
char CLS[256] ; //Device type
int PrnType ; //Printer type
} DEVICEID01, *LPDEVICEID01 ;
Java结构体1-2
public static class DEVICEID01 extends Structure {
public int Size;
public int Version;
public byte[] MFG = new byte[256];
public byte[] CMD = new byte[256];
public byte[] MDL = new byte[256];
public byte[] CLS = new byte[256];
public int PrnType;
public static class ByReference extends DEVICEID01 implements
Structure.ByReference {}
public static class ByValue extends DEVICEID01 implements
Structure.ByValue {}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
protected List getFieldOrder() {
List a = new ArrayList();
a.add("Size");
a.add("Version");
a.add("MFG");
a.add("CMD");
a.add("MDL");
a.add("CLS");
a.add("PrnType");
return a;
}
}
C++结构体2-1
//=====================================
// STATUS information
//=====================================
typedef struct _STATUSVERSION { //Status information version
WORD MajorVersion ; //Major version
WORD MinerVersion ; //miner version
}STATUSVERSION, *LPSTATUSVERSION ;
Java结构体2-1
public static class STATUSVERSION extends Structure {
public WORD MajorVersion;
public WORD MinerVersion;
public static class ByReference extends STATUSVERSION implements
Structure.ByReference {}
public static class ByValue extends STATUSVERSION implements
Structure.ByValue {}
public STATUSVERSION() {
super(ALIGN_NONE);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
protected List getFieldOrder() {
List a = new ArrayList();
a.add("MajorVersion");
a.add("MinerVersion");
return a;
}
}
C++结构体3-1
typedef struct _CARTRIDGEANDINKINFO { //Cartridge and ink information
BYTE CartridgeType ; //Cartridge name code
DWORD ColorType ; //Cartridge color code
BYTE InkRest ; //Ink rest information
BYTE InkDimension ; //Ink dimension information
} CARTRIDGEANDINKINFO, *LPCARTRIDGEANDINKINFO ;
Java结构体3-2
public static class CARTRIDGEANDINKINFO extends Structure {
public byte CartridgeType;
public DWORD ColorType;
public byte InkRest;
public byte InkDimension;
public static class ByReference extends CARTRIDGEANDINKINFO implements
Structure.ByReference {}
public static class ByValue extends CARTRIDGEANDINKINFO implements
Structure.ByValue {}
public CARTRIDGEANDINKINFO() {
super(ALIGN_NONE);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
protected List getFieldOrder() {
List a = new ArrayList();
a.add("CartridgeType");
a.add("ColorType");
a.add("InkRest");
a.add("InkDimension");
return a;
}
}
C++结构体4-1
typedef struct _CUSTOMSTATUS03_IJ{
DWORD Size ; // this struct size
STATUSVERSION Version ; // struct version
BYTE StatusReplyType; // status reply type
BYTE StatusCode ; // status code
BYTE ErrorCode ; // error code
BYTE WarningCode[16]; // warning code
CARTRIDGEANDINKINFO CartridgeInk[16]; // Ink Status
BYTE InkRemainInfo[3] ; // ink remain information
DWORD MonochromePrintedNumber; // monochrome printed number
DWORD ColorPrintedNumber; // color printed number
} CUSTOMSTATUS03_IJ, *LPCUSTOMSTATUS03_IJ;
Java结构体4-2
public static class CUSTOMSTATUS03_IJ extends Structure {
public DWORD Size;
public STATUSVERSION Version;
public byte StatusReplyType;
public byte StatusCode;
public byte ErrorCode;
public byte[] WarningCode = new byte[16];
public CARTRIDGEANDINKINFO[] CartridgeInk = new CARTRIDGEANDINKINFO[16];
public byte[] InkRemainInfo = new byte[3];
public DWORD MonochromePrintedNumber;
public DWORD ColorPrintedNumber;
public static class ByReference extends CUSTOMSTATUS03_IJ implements
Structure.ByReference {}
public static class ByValue extends CUSTOMSTATUS03_IJ implements
Structure.ByValue {}
public CUSTOMSTATUS03_IJ() {
super(ALIGN_NONE);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
protected List getFieldOrder() {
List a = new ArrayList();
a.add("Size");
a.add("Version");
a.add("StatusReplyType");
a.add("StatusCode");
a.add("ErrorCode");
a.add("WarningCode");
a.add("CartridgeInk");
a.add("InkRemainInfo");
a.add("MonochromePrintedNumber");
a.add("ColorPrintedNumber");
return a;
}
}
关于数组的处理
1. Java数组与C数组区别:
- Java的数组在栈里也许是连续的,但是指向的是堆里面的对象,所以不是连续的内存空间
-
而C的数组在内存里是连续的内存空间
Java数组与C数组示意.png
用以下代码创建数组错误:
CLibrary.OIDINFO[] oidInfo = new CLibrary.OIDINFO[100];
CLibrary.OIDINFO.ByReference[] oidInfo = new CLibrary.OIDINFO.ByReference[100];
用以下代码创建数组正确:
OIDINFO[] oidInfo = (OIDINFO[])new OIDINFO().toArray(100);
2. 关于byte[ ]
JNA 中,char * 和 char 类型都可以映射为 byte[] 类型
通过Java里面String里面的getBytes()方法可以快速将String转换为Byte数组
但是在JNA里面这样使用会有一定的问题
原因是C 中的 char 数组是以一个 NULL 字符作为结束中止标识, 使用Java 中的 getBytes() 并不会在byte 数组后加结束符,导致读取参数数组越界,强制结束。
JNA里面自带了toByteArray方法,使用toByteArray更稳妥。
以下代码比较了两种方式的区别:
public static String OID_REBOOT = "1.3.6.1.2.1.43.5.1.1.3.1";
byte[] a = TestEpson.OID_REBOOT.getBytes();
System.out.println(Arrays.toString(a));
\\输出[49, 46, 51, 46, 54, 46, 49, 46, 50, 46, 49, 46, 52, 51, 46, 53, 46, 49, 46, 49, 46, 51, 46, 49]
byte[] b = Native.toByteArray(TestEpson.OID_REBOOT);
System.out.println(Arrays.toString(b));
\\输出[49, 46, 51, 46, 54, 46, 49, 46, 50, 46, 49, 46, 52, 51, 46, 53, 46, 49, 46, 49, 46, 51, 46, 49, 0]
网友评论