赞
踩
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依赖:
<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 (
<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.
提供内部实用程序。 |
获取mac平台下的C共享库,然后调用printf函数打印。
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
自定义一个求和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
在JAVA中都是值传递,但是因为使用JNA框架,目标函数是C/C++是有地址变量的,很多时候都需要将变量的结果带回,因此,地址传递在JNA项目中几乎是必须的。
自定义求和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函数访问到奇怪的地址,导致报错。
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方法提供了为各种类型设置值的方法:
第一个参数为偏移量,第二个参数为值。
//设置int
int_pointer.setInt(0, 123);
//设置double
double_pointer.setDouble(0, 22.33);
Pointer的getXxx方法提供了为各种类型获取值的方法:
获取单个值的参数为偏移量,获取数组还需要传递一个获取数量。
//获取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);
//返回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!
JNA框架提供了com.sun.jna.ptr.ByReference
,引用对象类型,提供通用的“指向类型的指针”功能,通常在C代码中用于向调用方返回值以及函数结果。
在低版本的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版,并未发现垃圾值的问题。
ByReference提供了很多继承类,类似Pointer的指针属性,每种数据类型都对应子类供使用比如int的用IntByReference,字符串的使用PointerByReference。
创建ByReference:
//无参构造,默认值为0
IntByReference intRef = new IntByReference();
//有参构造,根据指定值创建
IntByReference intRef = new IntByReference(4);
设置/获取值:
//设置
intRef.setValue(10);
//获取
intRef.getValue();
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
总的来讲ByReference更加方便,但是对于多层的指针引用,可能Pointer更合适处理嵌套结构。
结构体作为参数有两个点,一个是传入,一个是返回。传入的还分传值和传引用。
目标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; }
函数内部的功能也简单:
编译为共享库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 + '\'' + "} " + 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 + '\'' + "} " + 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)) {
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)) {
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)) {
int tea_age@0x0=0x0012
String tea_name@0x8=小学生
}
stu-name=小学生/nstu-age=18/n
要使用 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 实例表示的是结构体指针还是结构体本身。
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
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
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
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
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
结构体中嵌套结构体数组用作输出参数时,需要对结构体数组的第一个元素赋初值。
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(); } } 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(); } } 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(); } } 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 }
文件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
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。
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);
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
类中变量名要和重写后的方法中的保持一致(名称不一致,变量个数不一致都会失败),否则编译不通过。
Java 中模拟结构体时,类名可以和 C 的结构体名称不同,只需要 Java 类的各个字段名称、 各字段顺序对应结构体中的字段即可。
文件array_param.c
#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 方法,这样实例化出来的数 组内存空间是连续的。
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 中将值赋给此指针变量,然后通过此指针变量获取值。
64 位的 jdk 只能调用 64 位的 dll,32 位也一样。如果使用的 jdk 和 dll 位数不同,会报 Unable to load DLL 'xxx.dll'
: 找不到指定的模块或者java.lang.UnsatisfiedLinkError: %1
不是有效的 Win32 应用程序。
.dll
.so
.dylib
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。