一,JNA的定义
JNA(Java Native Access)是一个开源的Java框架,是Sun公司推出的一个调用本地动态库的文件中方法的技术,是建立在经典的JNI基础上的一个框架。
优点:1,基本上在java环境就可以完成操作。
2,不需要重写动态库中的方法(jni需要为每个方法写一个jni方法),直接调用API。
缺点:1,有少许性能损失,但是总体影响不大。
2,只能实现java访问c,而不能实现c访问java。
二,JNA使用
1,JNA和C数据类型转换
Native Type
|
Size
|
Java Type
|
Common Windows Types
|
char |
8-bit integer
|
byte
|
BYTE,TCHAR
|
short
|
16-bit integer
|
short
|
WORD
|
wchar_t
|
16/32-bit character
|
char
|
TCHAR
|
int
|
32-bit integer
|
int
|
DWORD
|
int
|
boolean value
|
boolean
|
BOOL
|
long
|
32/64-bit integer
|
NativaLong
|
LONG
|
long long
|
64-bit integer
|
long
|
__int64
|
float
|
32-bit FP
|
float
|
|
double
|
64-bit FP
|
double
|
|
char *
|
C string
|
String
|
LPTCSTR
|
void *
|
pointer
|
Pointer
|
LPVOID,HANDLE,LPXXX
|
JNA还支持其他的类型转换,可以到 http://java-native-access.github.io/jna/4.4.0/javadoc/
查看。
2,JNA调用例子1
导入JNA的jar包,可以通过maven中添加:
<
dependency
>
<
groupId
>
com.sun.jna
groupId
>
<
artifactId
>
jna
artifactId
>
<
version
>
3.0.9
version
>
dependency
>
代码如下:
c接口:
int
add(
int
a,
int
b);
java代码:
public interface
TestInteface
extends
Library {
int
add(
int
a,
int
b);
}
public class
App
{
public static void
main( String[] args )
{
System.
load
(
"D:/vsproject/jnavctest/x64/Release/jnavctest.dll"
);
try
{
TestInteface INSTANCE = (TestInteface) Native.
loadLibrary
(
"jnavctest"
,
TestInteface.
class
);
System.
out
.println(INSTANCE.add(
6
,
77
));
}
catch
(Exception e1) {
System.
out
.println(e1.getMessage());
}
System.
out
.println(
"end"
);
}
}
程序解释:
(1)建立一个接口继承Library,默认继承是Library,如果c中是stdcall方式导出的,就继承StdCallLibrary。
public interface
TestInteface
extends
Library {
}
(2)声明方法,方法的返回值和参数要和本地dll对应,对应类型可以查看上节类型转换。
public interface
TestInteface
extends
Library {
int
add(
int
a,
int
b);
}
(3)实例化接口实例
TestInteface INSTANCE = (TestInteface) Native.
loadLibrary
(
"jnavctest"
,
TestInteface.
class
);
注意:1,要把动态库路径加载到系统路径中,
System.
load
(
"D:/vsproject/jnavctest/x64/Release/jnavctest.dll"
),这里为了方便采用的绝对路径。
2,实例化的第一个参数jnavctest不能带有.dll,java是跨平台,在Linux下面是so 。
(4)调用接口
INSTANCE.add(
6
,
77
);
3,JNA调用例子二
上面的例子比较简单,但是c中经常有结构体,指针等数据类型,我们调用一个结构体和指针的例子。
导入jar、实例化接口等参考上节。
代码如下:
c接口:
typedef struct{
byte b1;
byte b2[2048];
byte b3[2048];
}myUserStruct;
extern "C" __declspec(dllexport) int testStruct2(myUserStruct sturct);
extern "C" __declspec(dllexport) int testStruct3(myUserStruct *sturct, byte *a, int *length);
java代码:
public class myUserStruct extends Structure {
public byte b1;
public byte[] b2 = new byte[2048];
public byte[] b3 = new byte[2048];
public static class ByReference extends myUserStruct implements Structure.ByReference {}
public static class ByValue extends myUserStruct implements Structure.ByValue {}
}
public interface TestInteface extends Library {
int testStruct2(myUserStruct.ByValue userStruct);
int testStruct3(myUserStruct.ByReference userStruct, ByteByReference a, IntByReference length);
}
public static void main( String[] args )
{
System.load("D:/vsproject/jnavctest/x64/Release/jnavctest.dll");
System.out.println( "start" );
try {
TestInteface INSTANCE = (TestInteface) Native.loadLibrary("jnavctest", TestInteface.class);
byte[] array = new byte[]{0x31, 0x32, 0x33, 0x34};
myUserStruct.ByValue testStruct = new myUserStruct.ByValue();
testStruct.b1 = 0x31;
testStruct.b3 = array;
System.out.println(INSTANCE.testStruct2(testStruct));
myUserStruct.ByReference testStruct1 = new myUserStruct.ByReference();
testStruct1.b1 = 0x31;
testStruct1.b3 = new byte[2048];
testStruct1.b3[0] = 0x31;
ByteByReference reference1 = new ByteByReference();
Memory memory1 = new Memory(4);
reference1.setPointer(memory1);
IntByReference intByReference1 = new IntByReference(4);
int kkkkret = INSTANCE.testStruct3(testStruct1, reference1, intByReference1);
byte[] temp1 = new byte[4];
memory1.read(0, temp1, 0, 4);
System.out.println(Arrays.toString(temp1));
System.out.println(reference1.getValue());
System.out.println(kkkkret);
} catch (Exception e) {
System.out.println(e.getMessage());
}
System.out.println("end");
}
程序解释:
(1)结构体指针和值参数的传递
c中的值类型对应java的value类型,也就是值类型,指针类型则对应引用类型。自定义的结构要传值或引用就要继承Structure,并实现Structure.ByReference和Structure.ByValue实现为空即可。
public class myUserStruct extends Structure {
public byte b1;
public byte[] b2 = new byte[2048];
public byte[] b3 = new byte[2048];
public static class ByReference extends myUserStruct implements Structure.ByReference {}
public static class ByValue extends myUserStruct implements Structure.ByValue {}
(2)接口参数对应
结构体指针和结构体分别对应myUserStruct.ByReference和myUserStruct.ByValue。
基础类型可以查阅JNA文档。byte *对应ByteByReference,int *对应IntByReference。
public interface TestInteface extends Library {
int testStruct2(myUserStruct.ByValue userStruct);
int testStruct3(myUserStruct.ByReference userStruct, ByteByReference a, IntByReference length);
(3)结构体的调用
实例化结构体的ByValue并传入。
byte[] array = new byte[]{0x31, 0x32, 0x33, 0x34};
myUserStruct.ByValue testStruct = new myUserStruct.ByValue();
testStruct.b1 = 0x31;
testStruct.b3 = array;
System.out.println(INSTANCE.testStruct2(testStruct));
(4)byte,int指针的调用和读取数据
byte是一个数组,这里的例子中是通过一个int的引用的传入的长度,实际使用中应该传入数组的长度并不允许越界,当byte长度为4,c中操作了byte[5]就会出错。由于java中申请的内存空间并不是连续,而c操作数组类的数据是根据指针来操作的,所有要自己申请一个内存空间,并用setPointer方法把内存赋给数组。
ByteByReference reference1 = new ByteByReference();
Memory memory1 = new Memory(4);
reference1.setPointer(memory1);
读取数据的时候,要把内存中数据读取出来赋给一个数组,然后读取数组的数据即可。
byte[] temp1 = new byte[4];
memory1.read(0, temp1, 0, 4);
int是对单一一个int的引用,只用实例化IntByReference,并赋值即可。
IntByReference intByReference1 = new IntByReference(4);
读取单一一个int的数据只用调用getValue方法即可。
System.out.println(reference1.getValue());
(5)结构体指针的调用
只需实例化的结构体的ByReference的实例传入即可
myUserStruct.ByReference testStruct1 = new myUserStruct.ByReference();
testStruct1.b1 = 0x31;
testStruct1.b3 = new byte[2048];
testStruct1.b3[0] = 0x31;
int testStruct3(myUserStruct.ByReference userStruct, ByteByReference a, IntByReference length);
4,使用中问题的记录
(1)
Can't load IA 32-bit .dll on a AMD 64-bit platform
主要问题是dll是32位,本地的jdk是64位,可以编译64位的dll或者换32的jdk
三、参考文档
1,
JNI的替代者—使用JNA访问Java外部功能接口( http://www.cnblogs.com/lanxuezaipiao/p/3635556.html
)
2,java使用jna调用dll(http://www.cnblogs.com/feng-gamer/p/6109334.html)