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依赖:

<dependency>
    <groupId>net.java.dev.jna</groupId>
    <artifactId>jna</artifactId>
    <version>5.10.0</version>
</dependency>
Java Native Access  
PackageDescription
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所需的类型和功能映射器。

Platform Utilities  
PackageDescription
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.

提供集成的扩展拖放和丢弃功能,允许缩重拖动图像在所有平台上使用。

Platform Specific  
PackageDescription
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平台提供公共库映射。

Other Packages  
PackageDescription
com.sun.jna.internal
Provides internal utilities.

提供内部实用程序。

2、入门案例

2.1、示例一(调用系统共享库)

获取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

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方法提供了为各种类型设置值的方法:
在这里插入图片描述
第一个参数为偏移量,第二个参数为值。

//设置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);      

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:

//无参构造,默认值为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;
}

函数内部的功能也简单:

  1. 打印stu的内容【验证值传递是否正确】
  2. 把stu的内容复制给结果变量teacher,用于函数返回【验证是否能返回结构体】
  3. 改变传入结构体变量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 + '\'' +
                        "} " + 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

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.ByReferenceStructure.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();
            }
        }

        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
}

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

#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
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐