Java——本地方法(JNA)详解
吴声子夜歌 2024-08-23 08:05:04 阅读 91
目录
JNA1、概述2、入门案例2.1、示例一(调用系统共享库)2.2、示例二(调用自定义共享库)
2、指针参数Pointer2.1、使用场景2.2、Pointer类2.3、案例
3、引用对象ByReference3.1、使用场景3.2、ByReference类3.3、案例3.4、Pointer与ByReference对比
4、Java模拟C结构体4.1、使用场景4.2、Structure类4.3、结构体本身作为参数4.3、结构体指针作为参数4.4、嵌套结构体本身作为参数4.5、嵌套结构体指针作为参数4.6、结构体中嵌套结构体数组1)、作为输入参数2)、作为输出参数
4.7、结构体数组作为参数1)、错误的调用一2)、错误的调用二3)、正确的调用
5、常见问题5.1、Java 类的字段声明必须与重写方法保持一致5.2、Java映射C数组乱码问题5.3、Java 接收 C 函数返回类型为 char*5.4、动态库和 jdk 的位数必须匹配5.5、动态库版本必须和系统类型匹配
JNA
1、概述
JNA 全称 Java Native Access,是一个建立在经典的 JNI 技术之上的 Java 开源框架。JNA 提供一组 Java 工具类用于在运行期动态访问系统本地库(native library:如 Window 的 dll)而不需要编写任何 Native/JNI 代码。开发人员只要在一个 java 接口中描述目标 native library 的函数与结构,JNA 将自动实现 Java 接口到native function 的映射。
官方网站:https://github.com/java-native-access/jna
Maven依赖:
<code><dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>5.10.0</version>
</dependency>
Package | Description |
---|---|
com.sun.jna |
Provides simplified native library access.
提供简化的本机库访问权限。
|
com.sun.jna.ptr |
Provides various native pointer-to-type ( <code><type> *) representations.
提供各种母语指针到类型(<类型> *)表示。
|
com.sun.jna.win32 |
Provides type and function mappers required for standard APIs on the Windows platform.
提供Windows平台上标准API所需的类型和功能映射器。
|
Package | Description |
---|---|
com.sun.jna.platform |
Provides cross-platform utilities based on platform-specific libraries.
根据特定于平台的库提供跨平台实用程序。
|
com.sun.jna.platform.dnd |
Provides integrated, extended drag and drop functionality, allowing ghosted drag images to be used on all platforms.
提供集成的扩展拖放和丢弃功能,允许缩重拖动图像在所有平台上使用。
|
Package | Description |
---|---|
com.sun.jna.platform.linux |
Provides common library mappings for Linux.
为Linux提供公共图书馆映射。
|
com.sun.jna.platform.mac |
Provides common library mappings for the OS X platform.
为OS X平台提供公共库映射。
|
com.sun.jna.platform.unix |
Provides common library mappings for Unix and X11-based platforms.
为基于UNIX和X11的平台提供公共图书馆映射。
|
com.sun.jna.platform.unix.aix |
Provides common library mappings for the AIX platform.
为AIX平台提供公共库映射。
|
com.sun.jna.platform.unix.solaris |
Provides common library mappings for the Solaris (SunOS) platform.
为Solaris(Sunos)平台提供公共图书馆映射。
|
com.sun.jna.platform.win32 |
Provides common library mappings for the Windows platform.
为Windows平台提供公共库映射。
|
com.sun.jna.platform.win32.COM |
Provides common library mappings for Windows Component Object Model (COM).
为Windows组件对象模型(COM)提供公共库映射。
|
com.sun.jna.platform.win32.COM.tlb |
Provides common library mappings for COM Type Libraries.
为COM类型库提供公共库映射。
|
com.sun.jna.platform.win32.COM.tlb.imp |
Provides common library mappings for COM Type Library implementations.
为COM类型库实现提供公共库映射。
|
com.sun.jna.platform.win32.COM.util |
Provides COM Utilities
提供Com Utilities.
|
com.sun.jna.platform.win32.COM.util.annotation |
Provides COM Utility annotations
提供com实用程序注释
|
com.sun.jna.platform.wince |
Provides common library mappings for the Windows CE platform.
为Windows CE平台提供公共库映射。
|
Package | Description |
---|---|
com.sun.jna.internal |
Provides internal utilities.
提供内部实用程序。
|
2、入门案例
2.1、示例一(调用系统共享库)
获取mac平台下的C共享库,然后调用printf函数打印。
<code>public class HelloWorld { -- -->
/**
* 定义一个接口,默认的是继承Library,如果动态链接库里额函数是以stdcall方式输出的,那么就继承StdCallLibrary
* 这个接口对应一个动态连接文件(windows:.dll, linux:.so, mac:.dylib)
*/
public interface CLibrary extends Library {
/**
* 接口内部需要一个公共静态常量INSTANCE,通过这个常量就可以获得这个接口的实例,从而使用接口的方法,也就是调用外部dll/so/dylib的函数
* 该常量通过Native.load()这个API获得
* 第一个参数为共享库的名称(不带后缀)
* 第二个参数为本接口的Class类型,JNA通过这个这个Class类型,反射创建接口的实例
* 共享库的查找顺序是:
* 先从当前类的当前文件夹找,如果没找到
* 再从工程当前文件夹下面找,如果找不到
* 最后在当前平台下面去搜索
*/
CLibrary INSTANCE = Native.load("c", CLibrary.class);
/**
* 接口中只需要定义要用到的函数或者公共变量,不需要的可以不定义
* z注意参数和返回值的类型,应该和共享库中的函数诶行保持一致
*/
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]);
}
}
}
运行时指定参数为a b c d
,运行结果如下:
Hello,World
Argument 0:a
Argument 1:b
Argument 2:c
Argument 3:d
2.2、示例二(调用自定义共享库)
自定义一个求和C函数hello.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
int add(int a, int b)
{
return a + b;
}
编译为共享库hello.dylib:
gcc -I $JAVA_HOME/include -I $JAVA_HOME/include/darwin -dynamiclib -o hello.dylib hello.c
使用JNA调用该共享库中的add函数:
public class HelloJNA {
public interface LibraryAdd extends Library {
//使用绝对路径加载
LibraryAdd LIBRARY_ADD = Native.load("/Users/acton_zhang/J2EE/MavenWorkSpace/java_safe_demo/src/main/java/pers/zhang/jna/hello.dylib", LibraryAdd.class);
int add(int a, int b);
}
public static void main(String[] args) {
//调用映射的接口函数
int add = LibraryAdd.LIBRARY_ADD.add(10, 15);
System.out.println(add);
}
}
输出:
25
2、指针参数Pointer
在JAVA中都是值传递,但是因为使用JNA框架,目标函数是C/C++是有地址变量的,很多时候都需要将变量的结果带回,因此,地址传递在JNA项目中几乎是必须的。
2.1、使用场景
自定义求和C函数,文件add.c:
//返回a+b的值
//同时c和msg通过指针参数返回
int add(int a, int b, int *c, char **msg)
{
*c = (a + b) * 2;
char *string = "hello world!";
*msg = string;
return a + b;
}
编译为共享库add.dylib:
gcc -I $JAVA_HOME/include -I $JAVA_HOME/include/darwin -dynamiclib -o add.dylib add.c
使用JNA调用该共享库中的add函数:
public class AddTest {
public interface LibAdd extends Library {
LibAdd INSTANCE = Native.load("/Users/acton_zhang/J2EE/MavenWorkSpace/java_safe_demo/src/main/java/pers/zhang/jna/add.dylib", LibAdd.class);
int add(int a, int b, int c, String msg);
}
public static void main(String[] args) {
int c = 0;
String msg = "start";
int add = LibAdd.INSTANCE.add(10, 15, c, msg);
System.out.println("求和结果:" + add);
System.out.println("c:" + c);
System.out.println("msg:" + msg);
}
}
结果显而易见,无论add函数对c和msg做了何种改变,返回java中,值都不会变更。甚至会因为我们对c和msg赋值导致C函数访问到奇怪的地址,导致报错。
2.2、Pointer类
JNA框架提供了com.sun.jna.Pointer
,指针数据类型,用于匹配转换映射函数的指针变量。
创建Pointer:
//这样的指针变量定义很像C的写法,就是在定义的时候申请空间。
Pointer c = new Memory(50);
Pointer msg = new Memory(50);
Pointer映射指定的指针类型:
//映射C中的int*类型
//获取int类型在内存中需要的空间大小
int size = Native.getNativeSize(Integer.class);
//为Pointer开辟int类型需要的内存空间
Pointer int_pointer = new Memory(size);
//映射C中的double*类型
Pointer double_pointer = new Memory(Native.getNativeSize(Double.class));
Pointer设置/获取值:
Pointer的setXxx方法提供了为各种类型设置值的方法:
第一个参数为偏移量,第二个参数为值。
<code>//设置int
int_pointer.setInt(0, 123);
//设置double
double_pointer.setDouble(0, 22.33);
Pointer的getXxx方法提供了为各种类型获取值的方法:
获取单个值的参数为偏移量,获取数组还需要传递一个获取数量。
<code>//获取int
int anInt = int_pointer.getInt(0);
//获取double
double aDouble = double_pointer.getDouble(0);
释放Pointer:
Native.free(Pointer.nativeValue(c)); //手动释放内存
Pointer.nativeValue(c, 0); //避免Memory对象被GC时重复执行Nativ.free()方法
Native.free(Pointer.nativeValue(msg));
Pointer.nativeValue(msg, 0);
2.3、案例
//返回a+b的值
//同时c和msg通过指针参数返回
int add(int a, int b, int *c, char **msg)
{ -- -->
*c = (a + b) * 2;
char *string = "hello world!";
*msg = string;
return a + b;
}
public class AddTest {
public interface LibAdd extends Library {
LibAdd INSTANCE = Native.load("/Users/acton_zhang/J2EE/MavenWorkSpace/java_safe_demo/src/main/java/pers/zhang/jna/add.dylib", LibAdd.class);
int add(int a, int b, Pointer c, Pointer msg);
}
public static void main(String[] args) {
//int类型指针
Pointer c = new Memory(Native.getNativeSize(Integer.class));
//二级指针,所以嵌套Pointer
Pointer msg = new Memory(Native.getNativeSize(Pointer.class));
int add = LibAdd.INSTANCE.add(10, 15, c, msg);
System.out.println("求和结果:" + add);
System.out.println("c:" + c.getInt(0));
//msg实际是二级指针,所以要先获取一级指针,再获取值
System.out.println("msg:" + msg.getPointer(0).getString(0));
Native.free(Pointer.nativeValue(c)); //手动释放内存
Pointer.nativeValue(c, 0); //避免Memory对象被GC时重复执行Native.free()方法
Native.free(Pointer.nativeValue(msg));
Pointer.nativeValue(msg, 0);
}
}
输出:
求和结果:25
c:50
msg:hello world!
3、引用对象ByReference
JNA框架提供了com.sun.jna.ptr.ByReference
,引用对象类型,提供通用的“指向类型的指针”功能,通常在C代码中用于向调用方返回值以及函数结果。
3.1、使用场景
在低版本的JNA中,如果C中函数执行失败时,没有对指针进行处理,那么使用Pointer就会得到一个垃圾值。
C函数文件test.c:
int test_pointer(int a, int *b)
{
if (a < 0)
{
//未对*b进行处理
return -1;
}
*b = a;
return 0;
}
编译为共享库test.dylib:
gcc -I $JAVA_HOME/include -I $JAVA_HOME/include/darwin -dynamiclib -o test.dylib test.c
使用JNA调用该共享库中的test_pointer函数:
public class PointerTest {
public interface LibPointerTest extends Library {
LibPointerTest INSTANCE = Native.load("/Users/acton_zhang/J2EE/MavenWorkSpace/java_safe_demo/src/main/java/pers/zhang/jna/test.dylib", LibPointerTest.class);
int test_pointer(int a, Pointer b);
}
public static void main(String[] args) {
int a = -10;
Pointer b = new Memory(Native.getNativeSize(Integer.class));
int add = LibPointerTest.INSTANCE.test_pointer(a, b);
System.out.println(add);
System.out.println(a);
System.out.println(b.getInt(0));
}
}
输出:
-1
-10
0
本文使用的为5.10版,并未发现垃圾值的问题。
3.2、ByReference类
ByReference提供了很多继承类,类似Pointer的指针属性,每种数据类型都对应子类供使用比如int的用IntByReference,字符串的使用PointerByReference。
创建ByReference:
<code>//无参构造,默认值为0
IntByReference intRef = new IntByReference();
//有参构造,根据指定值创建
IntByReference intRef = new IntByReference(4);
设置/获取值:
//设置
intRef.setValue(10);
//获取
intRef.getValue();
3.3、案例
int test_pointer(int a, int *b)
{ -- -->
if (a < 0)
{
//未对*b进行处理
return -1;
}
*b = a;
return 0;
}
public class PointerTest {
public interface LibPointerTest extends Library {
LibPointerTest INSTANCE = Native.load("/Users/acton_zhang/J2EE/MavenWorkSpace/java_safe_demo/src/main/java/pers/zhang/jna/test.dylib", LibPointerTest.class);
int test_pointer(int a, ByReference b);
}
public static void main(String[] args) {
int a = 10;
IntByReference b = new IntByReference();
int add = LibPointerTest.INSTANCE.test_pointer(10, b);
System.out.println(add);
System.out.println(a);
System.out.println(b.getValue());
}
}
输出:
0
10
10
3.4、Pointer与ByReference对比
Pointer和ByReference都可以在JNA项目中用来地址传递参数Pointer使用方式类似C/C++需要手动分配/回收内存ByReference使用方式就是Java语法,内存通过垃圾回收机制自动完成
总的来讲ByReference更加方便,但是对于多层的指针引用,可能Pointer更合适处理嵌套结构。
4、Java模拟C结构体
4.1、使用场景
结构体作为参数有两个点,一个是传入,一个是返回。传入的还分传值和传引用。
目标C程序,struct.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// teacher 结构体定义
typedef struct {
int tea_age;
char *tea_name;
} Teacher;
// student 结构体定义
typedef struct {
int stu_age;
char *stu_name;
} Student;
Teacher stuTea(Student stu, Teacher *tea) {
printf("stu-name=%s/n", stu.stu_name);
printf("stu-age=%d/n", stu.stu_age);
// 将stu复制给tea做函数返回
Teacher teacher;
teacher.tea_name = stu.stu_name;
teacher.tea_age = stu.stu_age;
tea->tea_name = strcat(tea->tea_name, "是好老师");
return teacher;
}
函数内部的功能也简单:
打印stu的内容【验证值传递是否正确】把stu的内容复制给结果变量teacher,用于函数返回【验证是否能返回结构体】改变传入结构体变量tea的值【验证结构体引用传递是否生效】
编译为共享库struct.dylib:
gcc -I $JAVA_HOME/include -I $JAVA_HOME/include/darwin -dynamiclib -o struct.dylib struct.c
JNA调用:
public class StructTest {
//定义一个接口,描述本地库
public interface LibStruct extends Library {
LibStruct INSTANCE = Native.load("/Users/acton_zhang/J2EE/MavenWorkSpace/java_safe_demo/src/main/java/pers/zhang/jna/struct.dylib", LibStruct.class);
//定义结构体
class TeacherStruct extends Structure {
//结构体参数类型及顺序要严格按照C结构体的类型及顺序
public int tea_age;
public String tea_name;
// 定义值传递和指针传递类
public static class ByReference extends TeacherStruct implements Structure.ByReference {
//指针和引用的传递使用ByReference
}
public static class ByValue extends TeacherStruct implements Structure.ByValue {
//拷贝参数传递使用ByValue
}
//重写getFieldOrder获取字段列表, 很重要,没有会报错
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[]{ "tea_age", "tea_name"});
}
@Override
public String toString() {
return "TeacherStruct{" +
"tea_age=" + tea_age +
", tea_name='" + tea_name + '\'' +code>
"} " + super.toString();
}
}
//定义结构体
class StudentStruct extends Structure { -- -->
public int stu_age;
public String stu_name;
// 定义值传递和指针传递类
public static class ByReference extends StudentStruct implements Structure.ByReference {
//指针和引用的传递使用ByReference
}
public static class ByValue extends StudentStruct implements Structure.ByValue {
//拷贝参数传递使用ByValue
}
//重写getFieldOrder获取字段列表, 很重要,没有会报错
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[]{ "stu_age", "stu_name"});
}
@Override
public String toString() {
return "StudentStruct{" +
"stu_age=" + stu_age +
", stu_name='" + stu_name + '\'' +code>
"} " + super.toString();
}
}
//描述本地函数
TeacherStruct.ByValue stuTea(StudentStruct.ByValue stu, TeacherStruct.ByReference tea);
}
public static void main(String[] args) { -- -->
LibStruct.StudentStruct.ByValue stuByValue = new LibStruct.StudentStruct.ByValue();
LibStruct.TeacherStruct.ByReference teaByReference = new LibStruct.TeacherStruct.ByReference();
stuByValue.stu_age = 18;
stuByValue.stu_name = "小学生";
teaByReference.tea_age = 48;
teaByReference.tea_name = "高级教师";
// 调用函数之前
System.out.println("调用函数之前teaByReference:" + teaByReference.toString());
// 调用方法。返回结果
LibStruct.TeacherStruct.ByValue result = LibStruct.INSTANCE.stuTea(stuByValue, teaByReference);
// 查看传地址的teaByReference的值是否变更
System.out.println("调用函数之后teaByReference:" + teaByReference.toString());
// 函数返回结果
System.out.println("调用函数返回结果result.name:" + result.toString());
}
}
输出:
调用函数之前teaByReference:TeacherStruct{ tea_age=48, tea_name='高级教师'} StructTest$LibStruct$TeacherStruct$ByReference(auto-allocated@0x7ffc36203c40 (16 bytes)) { -- -->code>
int tea_age@0x0=0x0030
String tea_name@0x8=高级教师
}
调用函数之后teaByReference:TeacherStruct{ -- -->tea_age=48, tea_name='高级教师是好老师'} StructTest$LibStruct$TeacherStruct$ByReference(auto-allocated@0x7ffc36203c40 (16 bytes)) { -- -->code>
int tea_age@0x0=0x0030
String tea_name@0x8=高级教师是好老师
}
调用函数返回结果result.name:TeacherStruct{ -- -->tea_age=18, tea_name='小学生'} StructTest$LibStruct$TeacherStruct$ByValue(auto-allocated@0x7ffc3620b480 (16 bytes)) { -- -->code>
int tea_age@0x0=0x0012
String tea_name@0x8=小学生
}
stu-name=小学生/nstu-age=18/n
4.2、Structure类
要使用 Java 类模拟 C 的结构体,需要 Java 类继承Structure类。
必须要注意,Structure子类中的公共字段的顺序,必须与 C 语言中的结构的顺序保持一致,否则会报错!
因为,Java 调用动态链接库中的 C 函数,实际上就是一段内存作为函数的参数传递给 C 函数。动态链接库以为这个参数就是 C 语言传过来的参数。同时,C 语言的结构体是一个严格的规范,它定义了内存的次序。因此,JNA 中模拟的结构体的变量顺序绝对不能错。
如果一个 Struct 有 2 个 int 变量 int a, int b,如果 JNA 中的次序和 C 语言中的次序相反,那么不会报错,但是数据将会被传递到错误的字段中去。
另外,Structure类有两个内部接口Structure.ByReference
和Structure.ByValue
。
这两个接口仅仅是标记:
如果一个类实现 Structure.ByReference 接口,就表示这个类代表结构体指针。如果一个类实现 Structure.ByValue 接口,就表示这个类代表结构体本身。如果不实现这两个接口,那么就相当于你实现了 Structure.ByReference 接口。
使用这两个接口的实现类,可以明确定义我们的 Structure 实例表示的是结构体指针还是结构体本身。
4.3、结构体本身作为参数
struct_self_param.c
#include <stdio.h>
struct User { -- -->
long id;
char* name;
int age;
};
void sayUser(struct User user)
{
printf("id:%ld\n", user.id);
printf("name:%s\n", user.name);
printf("age:%d\n", user.age);
}
编译为共享库struct_self_param.dylib:
gcc -I $JAVA_HOME/include -I $JAVA_HOME/include/darwin -dynamiclib -o struct_self_param.dylib struct_self_param.c
JNA调用:
public class StructSelfParamTest {
//描述本地共享库
public interface LibStructSelfParam extends Library {
LibStructSelfParam INSTANCE = Native.load("/Users/acton_zhang/J2EE/MavenWorkSpace/java_safe_demo/src/main/java/pers/zhang/jna/struct_self_param.dylib", LibStructSelfParam.class);
//定义结构体
class UserStruct extends Structure {
//公共字段的顺序,必须与C语言中的结构的顺序保持一致
public NativeLong id;
public String name;
public int age;
// 定义值传递和指针传递类
public static class ByReference extends UserStruct implements Structure.ByReference {
//指针和引用的传递使用ByReference
}
public static class ByValue extends UserStruct implements Structure.ByValue {
//拷贝参数传递使用ByValue
}
// 重写getFieldOrder获取字段列表, 很重要,没有会报错
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[]{ "id", "name", "age"});
}
}
//描述本地函数,值传递
void sayUser(UserStruct.ByValue user);
}
public static void main(String[] args) {
LibStructSelfParam.UserStruct.ByValue user = new LibStructSelfParam.UserStruct.ByValue();
user.id = new NativeLong(10000);
user.name = "tom";
user.age = 18;
LibStructSelfParam.INSTANCE.sayUser(user);
}
}
输出:
id:10000
name:tom
age:18
4.3、结构体指针作为参数
struct_pointer_param.c
#include <stdio.h>
struct User {
long id;
char* name;
int age;
};
void sayUser(struct User* user)
{
printf("use strcture pointer\n");
printf("id:%ld\n", user->id);
printf("name:%s\n", user->name);
printf("age:%d\n", user->age);
}
编译为共享库struct_pointer_param.dylib:
gcc -I $JAVA_HOME/include -I $JAVA_HOME/include/darwin -dynamiclib -o struct_pointer_param.dylib struct_pointer_param.c
JNA调用:
public class StructPointerParamTest {
//描述本地共享库
public interface LibStructPointerParam extends Library {
LibStructPointerParam INSTANCE = Native.load("/Users/acton_zhang/J2EE/MavenWorkSpace/java_safe_demo/src/main/java/pers/zhang/jna/struct_pointer_param.dylib", LibStructPointerParam.class);
//定义结构体
class UserStruct extends Structure {
//公共字段的顺序,必须与C语言中的结构的顺序保持一致
public NativeLong id;
public String name;
public int age;
// 定义值传递和指针传递类
public static class ByReference extends StructSelfParamTest.LibStructSelfParam.UserStruct implements Structure.ByReference {
//指针和引用的传递使用ByReference
}
public static class ByValue extends StructSelfParamTest.LibStructSelfParam.UserStruct implements Structure.ByValue {
//拷贝参数传递使用ByValue
}
// 重写getFieldOrder获取字段列表, 很重要,没有会报错
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[]{ "id", "name", "age"});
}
}
//描述本地函数,指针传递
void sayUser(UserStruct.ByReference user);
}
public static void main(String[] args) {
LibStructPointerParam.UserStruct.ByReference user = new LibStructPointerParam.UserStruct.ByReference();
user.id = new NativeLong(10000);
user.name = "tom";
user.age = 18;
LibStructPointerParam.INSTANCE.sayUser(user);
}
}
输出:
use strcture pointer
id:10000
name:tom
age:18
4.4、嵌套结构体本身作为参数
C 语言最复杂的数据类型就是结构体。结构体的内部可以嵌套结构体,这使它可以模拟任何类型的对象。JNA 也可以模拟这类复杂的结构体,结构体内部可以包含结构体对象指针的数组。
struct_nested_param.c
#include <stdio.h>
struct User {
long id;
char* name;
int age;
};
struct Company {
long id;
const char* name;
struct User users[3];
int count;
};
void showNestedStruct(struct Company company)
{
printf("This is nested struct.\n");
printf("company id is:%ld\n", company.id);
printf("company name is:%s\n", company.name);
for (int i =0; i < 3; i++)
{
printf("user[%d] info of company\n", i);
printf("user id:%ld\n", company.users[i].id);
printf("user name:%s\n", company.users[i].name);
printf("user age:%d\n", company.users[i].age);
}
printf("count %d\n", company.count);
}
编译为共享库struct_nested_param.dylib:
gcc -I $JAVA_HOME/include -I $JAVA_HOME/include/darwin -dynamiclib -o struct_nested_param.dylib struct_nested_param.c
JNA调用:
public class StructNestedParamTest {
//描述本地共享库
public interface LibStructNestedParam extends Library {
LibStructNestedParam INSTANCE = Native.load("/Users/acton_zhang/J2EE/MavenWorkSpace/java_safe_demo/src/main/java/pers/zhang/jna/struct_nested_param.dylib", LibStructNestedParam.class);
//定义User结构体
class UserStruct extends Structure {
//公共字段的顺序,必须与C语言中的结构的顺序保持一致
public NativeLong id;
public String name;
public int age;
// 定义值传递和指针传递类
public static class ByReference extends UserStruct implements Structure.ByReference {
//指针和引用的传递使用ByReference
}
public static class ByValue extends UserStruct implements Structure.ByValue {
//拷贝参数传递使用ByValue
}
// 重写getFieldOrder获取字段列表, 很重要,没有会报错
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[]{ "id", "name", "age"});
}
}
//定义Company结构体
class CompanyStruct extends Structure {
//公共字段的顺序,必须与C语言中的结构的顺序保持一致
public NativeLong id;
public String name;
public UserStruct.ByValue[] users = new UserStruct.ByValue[3];
public int count;
// 定义值传递和指针传递类
public static class ByReference extends CompanyStruct implements Structure.ByReference {
//指针和引用的传递使用ByReference
}
public static class ByValue extends CompanyStruct implements Structure.ByValue {
//拷贝参数传递使用ByValue
}
// 重写getFieldOrder获取字段列表, 很重要,没有会报错
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[]{ "id", "name", "users", "count"});
}
}
//描述本地函数,值传递
void showNestedStruct(CompanyStruct.ByValue company);
}
public static void main(String[] args) {
LibStructNestedParam.UserStruct.ByValue user1 = new LibStructNestedParam.UserStruct.ByValue();
user1.id = new NativeLong(1);
user1.name = "zhangsan";
user1.age = 18;
LibStructNestedParam.UserStruct.ByValue user2 = new LibStructNestedParam.UserStruct.ByValue();
user2.id = new NativeLong(2);
user2.name = "lisi";
user2.age = 19;
LibStructNestedParam.UserStruct.ByValue user3 = new LibStructNestedParam.UserStruct.ByValue();
user3.id = new NativeLong(3);
user3.name = "wangwu";
user3.age = 20;
LibStructNestedParam.CompanyStruct.ByValue company = new LibStructNestedParam.CompanyStruct.ByValue();
company.id = new NativeLong(1000001);
company.name = "XXXXXX有限责任公司";
company.count = 3;
company.users[0] = user1;
company.users[1] = user2;
company.users[2] = user3;
LibStructNestedParam.INSTANCE.showNestedStruct(company);
}
}
输出:
This is nested struct.
company id is:1000001
company name is:XXXXXX有限责任公司
user[0] info of company
user id:1
user name:zhangsan
user age:18
user[1] info of company
user id:2
user name:lisi
user age:19
user[2] info of company
user id:3
user name:wangwu
user age:20
count 3
4.5、嵌套结构体指针作为参数
struct_nested_pointer_param.c
#include <stdio.h>
struct User {
long id;
char* name;
int age;
};
struct Company {
long id;
const char* name;
struct User users[3];
int count;
};
void showNestedStruct(struct Company* company)
{
printf("This is nested struct.\n");
printf("company id is:%ld\n", company->id);
printf("company name is:%s\n", company->name);
for (int i =0; i < 3; i++)
{
printf("user[%d] info of company\n", i);
printf("user id:%ld\n", company->users[i].id);
printf("user name:%s\n", company->users[i].name);
printf("user age:%d\n", company->users[i].age);
}
printf("count %d\n", company->count);
}
编译为共享库struct_nested_pointer_param.dylib:
gcc -I $JAVA_HOME/include -I $JAVA_HOME/include/darwin -dynamiclib -o struct_nested_pointer_param.dylib struct_nested_pointer_param.c
JNA调用:
public class StructNestedPointerParamTest {
//描述本地共享库
public interface LibStructNestedPointerParam extends Library {
LibStructNestedPointerParam INSTANCE = Native.load("/Users/acton_zhang/J2EE/MavenWorkSpace/java_safe_demo/src/main/java/pers/zhang/jna/struct_nested_pointer_param.dylib", LibStructNestedPointerParam.class);
//定义User结构体
class UserStruct extends Structure {
//公共字段的顺序,必须与C语言中的结构的顺序保持一致
public NativeLong id;
public String name;
public int age;
// 定义值传递和指针传递类
public static class ByReference extends UserStruct implements Structure.ByReference {
//指针和引用的传递使用ByReference
}
public static class ByValue extends UserStruct implements Structure.ByValue {
//拷贝参数传递使用ByValue
}
// 重写getFieldOrder获取字段列表, 很重要,没有会报错
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[]{ "id", "name", "age"});
}
}
//定义Company结构体
class CompanyStruct extends Structure {
//公共字段的顺序,必须与C语言中的结构的顺序保持一致
public NativeLong id;
public String name;
public UserStruct.ByValue[] users = new UserStruct.ByValue[3];
public int count;
// 定义值传递和指针传递类
public static class ByReference extends CompanyStruct implements Structure.ByReference {
//指针和引用的传递使用ByReference
}
public static class ByValue extends CompanyStruct implements Structure.ByValue {
//拷贝参数传递使用ByValue
}
// 重写getFieldOrder获取字段列表, 很重要,没有会报错
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[]{ "id", "name", "users", "count"});
}
}
//描述本地函数,指针传递
void showNestedStruct(CompanyStruct.ByReference company);
}
public static void main(String[] args) {
LibStructNestedPointerParam.UserStruct.ByValue user1 = new LibStructNestedPointerParam.UserStruct.ByValue();
user1.id = new NativeLong(1);
user1.name = "zhangsan";
user1.age = 18;
LibStructNestedPointerParam.UserStruct.ByValue user2 = new LibStructNestedPointerParam.UserStruct.ByValue();
user2.id = new NativeLong(2);
user2.name = "lisi";
user2.age = 19;
LibStructNestedPointerParam.UserStruct.ByValue user3 = new LibStructNestedPointerParam.UserStruct.ByValue();
user3.id = new NativeLong(3);
user3.name = "wangwu";
user3.age = 20;
LibStructNestedPointerParam.CompanyStruct.ByReference company = new LibStructNestedPointerParam.CompanyStruct.ByReference();
company.id = new NativeLong(1000001);
company.name = "XXXXXX有限责任公司";
company.count = 3;
company.users[0] = user1;
company.users[1] = user2;
company.users[2] = user3;
LibStructNestedPointerParam.INSTANCE.showNestedStruct(company);
}
}
输出:
This is nested struct.
company id is:1000001
company name is:XXXXXX有限责任公司
user[0] info of company
user id:1
user name:zhangsan
user age:18
user[1] info of company
user id:2
user name:lisi
user age:19
user[2] info of company
user id:3
user name:wangwu
user age:20
count 3
4.6、结构体中嵌套结构体数组
1)、作为输入参数
struct_nested_array_param.c
#include <stdio.h>
typedef struct {
int enable;
int x;
int y;
int width;
int height;
} area_pos;
typedef struct {
int enable;
int x;
int y;
} spot_pos;
typedef struct {
int enable;
int sta_x;
int sta_y;
int end_x;
int end_y;
} line_pos;
typedef struct {
area_pos area[2];
spot_pos spot[2];
line_pos line;
} image_pos;
void get_struct_array_value(image_pos *img_data){
printf("line_pos enable:%d\n",img_data->line.enable);
printf("line_pos sta_x:%d\n",img_data->line.sta_x);
printf("line_pos sta_y:%d\n",img_data->line.sta_y);
printf("line_pos end_x:%d\n",img_data->line.end_x);
printf("line_pos end_y:%d\n",img_data->line.end_y);
for (int i = 0; i<2;i++){
printf("area_pos[%d] enable:%d\n",i,img_data->area[i].enable);
printf("area_pos[%d] x:%d\n",i,img_data->area[i].x);
printf("area_pos[%d] y:%d\n",i,img_data->area[i].y);
printf("area_pos[%d] width:%d\n",i,img_data->area[i].width);
printf("area_pos[%d] height:%d\n",i,img_data->area[i].height);
}
for (int j = 0; j < 2; j++){
printf("spot_pos[%d] enable:%d\n",j,img_data->spot[j].enable);
printf("spot_pos[%d] x:%d\n",j,img_data->spot[j].x);
printf("spot_pos[%d] y:%d\n",j,img_data->spot[j].y);
}
}
编译为共享库struct_nested_array_param.dylib:
gcc -I $JAVA_HOME/include -I $JAVA_HOME/include/darwin -dynamiclib -o struct_nested_array_param.dylib struct_nested_array_param.c
JNA调用:
public class StructNestedArrayParamTest {
public interface LibStructNestedArrayParam extends Library {
LibStructNestedArrayParam INSTANCE = Native.load("/Users/acton_zhang/J2EE/MavenWorkSpace/java_safe_demo/src/main/java/pers/zhang/jna/struct_nested_array_param.dylib", LibStructNestedArrayParam.class);
class AreaPos extends Structure {
public int enable;
public int x;
public int y;
public int width;
public int height;
public static class ByReference extends AreaPos implements Structure.ByReference { }
public static class ByValue extends AreaPos implements Structure.ByValue { }
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[]{ "enable", "x", "y", "width", "height"});
}
}
class SpotPos extends Structure {
public int enable;
public int x;
public int y;
public static class ByReference extends SpotPos implements Structure.ByReference { }
public static class ByValue extends SpotPos implements Structure.ByValue { }
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[]{ "enable", "x", "y"});
}
}
class LinePos extends Structure {
public int enable;
public int sta_x;
public int sta_y;
public int end_x;
public int end_y;
public static class ByReference extends LinePos implements Structure.ByReference { }
public static class ByValue extends LinePos implements Structure.ByValue { }
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[]{ "enable", "sta_x", "sta_y", "end_x", "end_y"});
}
}
class ImagePos extends Structure {
public AreaPos.ByValue[] area = new AreaPos.ByValue[2];
public SpotPos.ByValue[] spot = new SpotPos.ByValue[2];
public LinePos.ByValue line;
public static class ByReference extends ImagePos implements Structure.ByReference { }
public static class ByValue extends ImagePos implements Structure.ByValue { }
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[]{ "area", "spot", "line"});
}
}
void get_struct_array_value(ImagePos.ByReference img);
}
public static void main(String[] args) {
LibStructNestedArrayParam.AreaPos.ByValue a1 = new LibStructNestedArrayParam.AreaPos.ByValue();
a1.enable = 1;
a1.x = 10;
a1.y = 20;
a1.height = 1080;
a1.width = 1920;
LibStructNestedArrayParam.AreaPos.ByValue a2 = new LibStructNestedArrayParam.AreaPos.ByValue();
a1.enable = 0;
a1.x = 20;
a1.y = 10;
a1.height = 1920;
a1.width = 1080;
LibStructNestedArrayParam.SpotPos.ByValue s1 = new LibStructNestedArrayParam.SpotPos.ByValue();
s1.enable = 0;
s1.x = 1;
s1.y = 1;
LibStructNestedArrayParam.SpotPos.ByValue s2 = new LibStructNestedArrayParam.SpotPos.ByValue();
s1.enable = 1;
s1.x = 2;
s1.y = 2;
LibStructNestedArrayParam.LinePos.ByValue line = new LibStructNestedArrayParam.LinePos.ByValue();
line.enable = 0;
line.end_x = 10;
line.end_y = 20;
line.sta_x = 30;
line.sta_y = 40;
LibStructNestedArrayParam.ImagePos.ByReference img = new LibStructNestedArrayParam.ImagePos.ByReference();
img.area[0] = a1;
img.area[1] = a2;
img.spot[0] = s1;
img.spot[1] = s2;
img.line = line;
LibStructNestedArrayParam.INSTANCE.get_struct_array_value(img);
}
}
输出:
line_pos enable:0
line_pos sta_x:30
line_pos sta_y:40
line_pos end_x:10
line_pos end_y:20
area_pos[0] enable:0
area_pos[0] x:20
area_pos[0] y:10
area_pos[0] width:1080
area_pos[0] height:1920
area_pos[1] enable:0
area_pos[1] x:0
area_pos[1] y:0
area_pos[1] width:0
area_pos[1] height:0
spot_pos[0] enable:1
spot_pos[0] x:2
spot_pos[0] y:2
spot_pos[1] enable:0
spot_pos[1] x:0
spot_pos[1] y:0
2)、作为输出参数
结构体中嵌套结构体数组用作输出参数时,需要对结构体数组的第一个元素赋初值。
struct_nested_array_out.c
#include <stdio.h>
typedef struct {
int enable;
int x;
int y;
int width;
int height;
} area_pos;
typedef struct {
int enable;
int x;
int y;
} spot_pos;
typedef struct {
int enable;
int sta_x;
int sta_y;
int end_x;
int end_y;
} line_pos;
typedef struct {
area_pos area[2];
spot_pos spot[2];
line_pos line;
} image_pos;
void set_struct_array_value(image_pos *img_data){
area_pos a1,a2;
a1.enable = 1;
a1.x = 10;
a1.y = 20;
a1.height = 1090;
a1.width = 1920;
a2.enable = 0;
a2.x = 20;
a2.y = 10;
a2.height = 1920;
a2.width = 1080;
spot_pos s1,s2;
s1.enable = 0;
s1.x = 1;
s1.y = 1;
s2.enable = 1;
s2.x = 2;
s2.y = 2;
line_pos l;
l.enable = 0;
l.end_x = 10;
l.end_y = 20;
l.sta_x = 30;
l.sta_y = 40;
img_data->area[0] = a1;
img_data->area[1] = a2;
img_data->spot[0] = s1;
img_data->spot[1] = s2;
img_data->line = l;
}
编译为共享库struct_nested_array_out.dylib:
gcc -I $JAVA_HOME/include -I $JAVA_HOME/include/darwin -dynamiclib -o struct_nested_array_out.dylib struct_nested_array_out.c
JNA调用:
public class StructNestedArrayOutTest {
public interface LibStructNestedArrayOut extends Library {
LibStructNestedArrayOut INSTANCE = Native.load("/Users/acton_zhang/J2EE/MavenWorkSpace/java_safe_demo/src/main/java/pers/zhang/jna/struct_nested_array_out.dylib", LibStructNestedArrayOut.class);
class AreaPos extends Structure {
public int enable;
public int x;
public int y;
public int width;
public int height;
public static class ByReference extends AreaPos implements Structure.ByReference { }
public static class ByValue extends AreaPos implements Structure.ByValue { }
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[]{ "enable", "x", "y", "width", "height"});
}
@Override
public String toString() {
return "AreaPos{" +
"enable=" + enable +
", x=" + x +
", y=" + y +
", width=" + width +
", height=" + height +
"} " + super.toString();code>
}
}
class SpotPos extends Structure { -- -->
public int enable;
public int x;
public int y;
public static class ByReference extends SpotPos implements Structure.ByReference { }
public static class ByValue extends SpotPos implements Structure.ByValue { }
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[]{ "enable", "x", "y"});
}
@Override
public String toString() {
return "SpotPos{" +
"enable=" + enable +
", x=" + x +
", y=" + y +
"} " + super.toString();code>
}
}
class LinePos extends Structure { -- -->
public int enable;
public int sta_x;
public int sta_y;
public int end_x;
public int end_y;
public static class ByReference extends LinePos implements Structure.ByReference { }
public static class ByValue extends LinePos implements Structure.ByValue { }
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[]{ "enable", "sta_x", "sta_y", "end_x", "end_y"});
}
@Override
public String toString() {
return "LinePos{" +
"enable=" + enable +
", sta_x=" + sta_x +
", sta_y=" + sta_y +
", end_x=" + end_x +
", end_y=" + end_y +
"} " + super.toString();code>
}
}
class ImagePos extends Structure { -- -->
public AreaPos.ByValue[] area = new AreaPos.ByValue[2];
public SpotPos.ByValue[] spot = new SpotPos.ByValue[2];
public LinePos.ByValue line;
public static class ByReference extends ImagePos implements Structure.ByReference { }
public static class ByValue extends ImagePos implements Structure.ByValue { }
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[]{ "area", "spot", "line"});
}
}
void set_struct_array_value(ImagePos.ByReference img);
}
public static void main(String[] args) {
LibStructNestedArrayOut.ImagePos.ByReference img = new LibStructNestedArrayOut.ImagePos.ByReference();
// img.area[0] = new LibStructNestedArrayOut.AreaPos.ByValue();
// img.spot[0] = new LibStructNestedArrayOut.SpotPos.ByValue();
LibStructNestedArrayOut.INSTANCE.set_struct_array_value(img);
for (int i = 0; i < 2; i++)
System.out.println(img.area[i]);
for (int i = 0; i < 2; i++)
System.out.println(img.spot[i]);
System.out.println(img.line);
}
}
如果未对数组第一项赋值,会报java.lang.IndexOutOfBoundsException
Exception in thread "main" java.lang.IndexOutOfBoundsException: Bounds exceeds available space : size=20, offset=40
所以一定要对输出参数中的数组第一项赋值:
public static void main(String[] args) {
LibStructNestedArrayOut.ImagePos.ByReference img = new LibStructNestedArrayOut.ImagePos.ByReference();
img.area[0] = new LibStructNestedArrayOut.AreaPos.ByValue();
img.spot[0] = new LibStructNestedArrayOut.SpotPos.ByValue();
LibStructNestedArrayOut.INSTANCE.set_struct_array_value(img);
for (int i = 0; i < 2; i++)
System.out.println(img.area[i]);
for (int i = 0; i < 2; i++)
System.out.println(img.spot[i]);
System.out.println(img.line);
}
输出:
AreaPos{ enable=1, x=10, y=20, width=1920, height=1090} StructNestedArrayOutTest$LibStructNestedArrayOut$AreaPos$ByValue(allocated@0x7fb3b0d1bd10 (20 bytes) (shared from auto-allocated@0x7fb3b0d1bd10 (84 bytes))) {
int enable@0x0=0x0001
int x@0x4=0x000A
int y@0x8=0x0014
int width@0xC=0x0780
int height@0x10=0x0442
}
AreaPos{ enable=0, x=20, y=10, width=1080, height=1920} StructNestedArrayOutTest$LibStructNestedArrayOut$AreaPos$ByValue(allocated@0x7fb3b0d1bd24 (20 bytes) (shared from auto-allocated@0x7fb3b0d1bd10 (84 bytes))) {
int enable@0x0=0x0000
int x@0x4=0x0014
int y@0x8=0x000A
int width@0xC=0x0438
int height@0x10=0x0780
}
SpotPos{ enable=0, x=1, y=1} StructNestedArrayOutTest$LibStructNestedArrayOut$SpotPos$ByValue(allocated@0x7fb3b0d1bd38 (12 bytes) (shared from auto-allocated@0x7fb3b0d1bd10 (84 bytes))) {
int enable@0x0=0x0000
int x@0x4=0x0001
int y@0x8=0x0001
}
SpotPos{ enable=1, x=2, y=2} StructNestedArrayOutTest$LibStructNestedArrayOut$SpotPos$ByValue(allocated@0x7fb3b0d1bd44 (12 bytes) (shared from auto-allocated@0x7fb3b0d1bd10 (84 bytes))) {
int enable@0x0=0x0001
int x@0x4=0x0002
int y@0x8=0x0002
}
LinePos{ enable=0, sta_x=30, sta_y=40, end_x=10, end_y=20} StructNestedArrayOutTest$LibStructNestedArrayOut$LinePos$ByValue(allocated@0x7fb3b0d1bd50 (20 bytes) (shared from auto-allocated@0x7fb3b0d1bd10 (84 bytes))) {
int enable@0x0=0x0000
int sta_x@0x4=0x001E
int sta_y@0x8=0x0028
int end_x@0xC=0x000A
int end_y@0x10=0x0014
}
4.7、结构体数组作为参数
文件struct_array_param.c
#include <stdio.h>
#include <string.h>
typedef struct {
int age;
char name[20];
} Person;
int changeObjs(Person per[], int size)
{
if (size <= 0)
{
return -1;
}
for (int i = 0; i < size; i++)
{
per[i].age *= 10;
strcpy(per[i].name, "wokettas");
}
for (int k = 0; k < size; k++)
{
printf("person[%d] age:%d\n", k, per[k].age);
printf("person[%d] name:%s\n", k, per[k].name);
}
return 0;
}
编译为共享库struct_array_param.dylib:
gcc -I $JAVA_HOME/include -I $JAVA_HOME/include/darwin -dynamiclib -o struct_array_param.dylib struct_array_param.c
1)、错误的调用一
public class StructArrayParamTest {
public interface LibStructArrayParam extends Library {
LibStructArrayParam INSTANCE = Native.load("/Users/acton_zhang/J2EE/MavenWorkSpace/java_safe_demo/src/main/java/pers/zhang/jna/struct_array_param.dylib", LibStructArrayParam.class);
class Person extends Structure {
public int age;
public byte[] name = new byte[20];
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[]{ "age", "name"});
}
//值传递
public static class ByValue extends Person implements Structure.ByValue { }
}
//描述函数
int changeObjs(Person.ByValue[] per, int size);
}
public static void main(String[] args) {
LibStructArrayParam.Person.ByValue[] per = new LibStructArrayParam.Person.ByValue[2];
LibStructArrayParam.Person.ByValue p1 = new LibStructArrayParam.Person.ByValue();
LibStructArrayParam.Person.ByValue p2 = new LibStructArrayParam.Person.ByValue();
p1.age = 1;
p1.name = Arrays.copyOf("tom".getBytes(), 20);
p2.age = 2;
p2.name = Arrays.copyOf("jerry".getBytes(), 20);
per[0] = p1;
per[1] = p2;
LibStructArrayParam.INSTANCE.changeObjs(per, 2);
}
}
如果在 Java 接口声明中错误把参数类型写成Person.ByValue
,会报java.lang.IndexOutOfBoundsException
。将参数类型改为结构体本身即可,即不带 ByReference 或 ByValue。结构体数组做参数时, 要区别于非数组的 ByReference 和 ByValue。
2)、错误的调用二
public class StructArrayParamTest {
public interface LibStructArrayParam extends Library {
LibStructArrayParam INSTANCE = Native.load("/Users/acton_zhang/J2EE/MavenWorkSpace/java_safe_demo/src/main/java/pers/zhang/jna/struct_array_param.dylib", LibStructArrayParam.class);
class Person extends Structure {
public int age;
public byte[] name = new byte[20];
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[]{ "age", "name"});
}
}
//描述函数
int changeObjs(Person[] per, int size);
}
public static void main(String[] args) {
LibStructArrayParam.Person[] per = new LibStructArrayParam.Person[2];
LibStructArrayParam.Person p1 = new LibStructArrayParam.Person();
LibStructArrayParam.Person p2 = new LibStructArrayParam.Person();
p1.age = 1;
p1.name = Arrays.copyOf("tom".getBytes(), 20);
p2.age = 2;
p2.name = Arrays.copyOf("jerry".getBytes(), 20);
per[0] = p1;
per[1] = p2;
LibStructArrayParam.INSTANCE.changeObjs(per, 2);
}
}
会报错Exception in thread "main" java.lang.IllegalArgumentException: Structure array elements must use contiguous memory (bad backing address at Structure array index 1)
结构体数组必须使用连续的内存区域。p1,p2 都是 new 出来的对象,不可能连续,用传统方式初始化数组不能解决。
应使用JNA提供的toArray方法:
public Structure[] toArray(int size);
3)、正确的调用
public class StructArrayParamTest {
public interface LibStructArrayParam extends Library {
LibStructArrayParam INSTANCE = Native.load("/Users/acton_zhang/J2EE/MavenWorkSpace/java_safe_demo/src/main/java/pers/zhang/jna/struct_array_param.dylib", LibStructArrayParam.class);
class Person extends Structure {
public int age;
public byte[] name = new byte[20];
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[]{ "age", "name"});
}
}
//描述函数
int changeObjs(Person[] per, int size);
}
public static void main(String[] args) {
LibStructArrayParam.Person per = new LibStructArrayParam.Person();
LibStructArrayParam.Person[] pers = (LibStructArrayParam.Person[]) per.toArray(2);
pers[0].age = 1;
pers[0].name = Arrays.copyOf("tom".getBytes(), 20);
pers[1].age = 2;
pers[1].name = Arrays.copyOf("jerry".getBytes(), 20);
LibStructArrayParam.INSTANCE.changeObjs(pers, 2);
}
}
输出:
person[0] age:10
person[0] name:wokettas
person[1] age:20
person[1] name:wokettas
5、常见问题
5.1、Java 类的字段声明必须与重写方法保持一致
类中变量名要和重写后的方法中的保持一致(名称不一致,变量个数不一致都会失败),否则编译不通过。
Java 中模拟结构体时,类名可以和 C 的结构体名称不同,只需要 Java 类的各个字段名称、 各字段顺序对应结构体中的字段即可。
5.2、Java映射C数组乱码问题
文件array_param.c
<code>#include <stdio.h>
#include <string.h>
typedef struct { -- -->
int enable;
char static_ip[20];
char netmask[20];
char gateway[20];
char dns1[20];
char dns2[20];
}network_eth;
int sdk_set_network_eth(const char *ip, network_eth *network_param)
{
if (strlen(ip) == 0 || network_param == NULL)
{
printf("sdk_set_network_eth param error!\n");
return -1;
}
printf("ip:%s\n",ip);
printf("network_eth enable:%d\n",network_param->enable);
printf("network_eth static_ip:%s\n",network_param->static_ip);
printf("network_eth netmask:%s\n",network_param->netmask);
printf("network_eth gateway:%s\n",network_param->gateway);
printf("network_eth dns1:%s\n",network_param->dns1);
printf("network_eth dns2:%s\n",network_param->dns2);
return 0;
}
编译为共享库array_param.dylib:
gcc -I $JAVA_HOME/include -I $JAVA_HOME/include/darwin -dynamiclib -o array_param.dylib array_param.c
JNA调用:
public class CharArrayTest {
public interface LibCharArray extends Library {
LibCharArray INSTANCE = Native.load("/Users/acton_zhang/J2EE/MavenWorkSpace/java_safe_demo/src/main/java/pers/zhang/jna/array_param.dylib", LibCharArray.class);
class NetWorkEth extends Structure {
public int enable;
public byte[] static_ip = new byte[20];
public byte[] netmask = new byte[20];
public byte[] gateway = new byte[20];
public byte[] dns1 = new byte[20];
public byte[] dns2 = new byte[20];
public static class ByReference extends NetWorkEth implements Structure.ByReference{ }
public static class ByValue extends NetWorkEth implements Structure.ByValue{ }
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[] { "enable","static_ip","netmask","gateway","dns1","dns2"});
}
}
int sdk_set_network_eth(byte[] ip, NetWorkEth.ByReference netWork);
}
public static void main(String[] args) {
String ip = "127.0.0.1";
LibCharArray.NetWorkEth.ByReference netWorkEth = new LibCharArray.NetWorkEth.ByReference();
netWorkEth.enable = 1;
netWorkEth.static_ip = "10.20.6.10".getBytes();
netWorkEth.netmask = "255.255.255.0".getBytes();
netWorkEth.gateway = "192.168.122.134".getBytes();
netWorkEth.dns1 = "114.114.114.114".getBytes();
netWorkEth.dns2 = "8.8.8.8".getBytes();
int res = LibCharArray.INSTANCE.sdk_set_network_eth(ip.getBytes(), netWorkEth);
System.out.println(res);
}
输出:
0
ip:127.0.0.1
network_eth enable:1
network_eth static_ip:10.20.6.10255.255.255.0192.168.122.134114.114.114.1148.8.8.8127.0.0.1
network_eth netmask:5.0192.168.122.134114.114.114.1148.8.8.8127.0.0.1
network_eth gateway:4.114.114.1148.8.8.8127.0.0.1
network_eth dns1:127.0.0.1
network_eth dns2:��#mRealVarArgsCheckerlang/O
输出乱码!因为C/C++的数组类型在内存中是连续存储的,而Java的数组不一定是连续的。对C中的char数组类型赋值时,不能直接给数组赋值,要使用Arrays.copyOf(String.getBytes(), 20)
赋值,数组长度和C结构体中声明的长度保持一致。
在某些情况下,虽然使用 String.getBytes()转换也能成功,但大多数情况下,使用该方法会出 现乱码,不建议使用 String.getBytes()。
public static void main(String[] args) {
String ip = "127.0.0.1";
byte[] ipArr = Arrays.copyOf(ip.getBytes(), 20);
LibCharArray.NetWorkEth.ByReference netWorkEth = new LibCharArray.NetWorkEth.ByReference();
netWorkEth.enable = 1;
netWorkEth.static_ip = Arrays.copyOf("10.20.6.10".getBytes(), 20);
netWorkEth.netmask = Arrays.copyOf("255.255.255.0".getBytes(), 20);
netWorkEth.gateway = Arrays.copyOf("192.168.122.134".getBytes(), 20);
netWorkEth.dns1 = Arrays.copyOf("114.114.114.114".getBytes(), 20);
netWorkEth.dns2 = Arrays.copyOf("8.8.8.8".getBytes(), 20);
int res = LibCharArray.INSTANCE.sdk_set_network_eth(ipArr, netWorkEth);
System.out.println(res);
}
输出:
0
ip:127.0.0.1
network_eth enable:1
network_eth static_ip:10.20.6.10
network_eth netmask:255.255.255.0
network_eth gateway:192.168.122.134
network_eth dns1:114.114.114.114
network_eth dns2:8.8.8.8
创建结构体数组应该使用 JNA 的 toArray()方法,而不是 Java 常规创建数组的方法,因为内 存空间在 java 中是不连续的,jna 定义数组是需要使用 toArray 方法,这样实例化出来的数 组内存空间是连续的。
5.3、Java 接收 C 函数返回类型为 char*
JNA 使用 String 无法直接接收 C 函数返回类型为 char*的值,必须要用 Pointer 进行接收。
当 C 函数的参数为 char*、const char*用做输入时,JNA 可以使用 String 类型进行传 参,此时可以正常调用 C 函数;但当C函数的参数类型为char*且用作输出时,使用 String 类型无法正常接收,必须使用 Pointer 进行处理。
文件char_out.c
#include <stdio.h>
#include <string.h>
void append_str(char* str, char* append)
{
printf("str:%s\n", str);
printf("append:%s\n", append);
int length = strlen(str);
char* p = str;
for(int i = 0; i < length; i++)
{
p++;
}
strcpy(p, append);
}
编译为共享库char_out.dylib:
gcc -I $JAVA_HOME/include -I $JAVA_HOME/include/darwin -dynamiclib -o char_out.dylib char_out.c
JNA调用,使用Pointer接收char*返回值:
public class CharOutTest {
public interface LibCharOut extends Library {
LibCharOut INSTANCE = Native.load("/Users/acton_zhang/J2EE/MavenWorkSpace/java_safe_demo/src/main/java/pers/zhang/jna/char_out.dylib", LibCharOut.class);
void append_str(Pointer str, String append);
}
public static void main(String[] args) {
Pointer str = new Memory(40);
str.setString(0, "hello");
String append = " world";
LibCharOut.INSTANCE.append_str(str, append);
System.out.println(str.getString(0));
}
}
输出:
hello world
str:hello
append: world
通过 Java 获取 char * 字符串,必须要通过 Java 传入一个 com.sun.jna.Pointer 指针变量,然后在 DLL 中将值赋给此指针变量,然后通过此指针变量获取值。
5.4、动态库和 jdk 的位数必须匹配
64 位的 jdk 只能调用 64 位的 dll,32 位也一样。如果使用的 jdk 和 dll 位数不同,会报 Unable to load DLL 'xxx.dll'
: 找不到指定的模块或者java.lang.UnsatisfiedLinkError: %1
不是有效的 Win32 应用程序。
5.5、动态库版本必须和系统类型匹配
windows:后缀名为.dll
linux:后缀名为.so
mac:后缀名为.dylib
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。