JNI便捷开发框架JNA框架之结构参数体传递(四)

一、背景

上一篇介绍了JNA框架开发的指针参数传递另一种方法ByReference。有了ByReference基本数据类型参数的传值,传地址的难题已经基本解决。
但是在实际生产过程中,还有一种数据类型也经常用到,在java中,我们叫对象,在C中对应的就是结构体。

事实上,在面向对象开发的过程中,很多时候不会单一的使用基本数据类型传参,经常都是用对象定义的变量当作参数传递。即使在面向过程的C语言中,面对复杂的业务场景,用结构体传参也是常有的是。现在的专题研究的是JNA,java和C交互。那么这个知识点就必须整好!!

结构体作为参数有两个点,一个是传入,一个是返回。传入的还分传值和传引用。接下来,我就用一个例子把这三个知识点一次性验证。


二、案例

现有目标C程序,hello.c


#include 
#include 
#include 

// teacher 结构体定义
typedef struct {
    int tea_age;
    char *tea_name;
} Teacher;

// student 结构体定义
typedef struct {
    int stu_age;
    char *stu_name;
} Student;

/**
 * 返回a+b的值
 * 同时c和msg通过参数返回
 */
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;
}

里面有个函数stuTea,函数有两个参数,Student ,Teacher * ,看得出来,一个是值接收,一个是引用接收。函数的返回类型是Teacher 。这里总共包含2个结构体,里面都有变量int和char *

// teacher 结构体定义
typedef struct {
    int tea_age;
    char *tea_name;
} Teacher;

// student 结构体定义
typedef struct {
    int stu_age;
    char *stu_name;
} Student;

函数内部的功能也简单:
①打印stu的内容【验证值传递是否正确】
②把stu的内容复制给结果变量teacher,用于函数返回【验证是否能返回结构体】
③改变传入结构体变量tea的值【验证结构体引用传递是否生效】

三、验证

首先我们将hello.c先生成动态链接库文件,这里我们换个方式,生成Windows下的dll
Ctrl+R 输入cmd ,然后cd到hello.c所在的目录,我这里是
F:\JNATestC\src\main\java\c_jna_2
然后运行命令 gcc -shared -o libhello.dll hello.c
在这里插入图片描述
此时有文件libhello.dll生成
(如果想引用LINUX下的动态链接库文件SO, 请回看我之前的文章【点击访问】)


下面开发java程序用于调用libhello.dll

HelloJNA_struct.java

package java_jna_2;

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Structure;

import java.util.Arrays;
import java.util.List;

/**
 * 一个java类
 * 演示结构体传递参数,地址传递,指针传递,结构体返回
 */
public class HelloJNA_struct {

    /**
     * 定义一个接口,默认的是继承Library ,如果动态链接库里的函数是以stdcall方式输出的,那么就继承StdCallLibrary
     * 这个接口对应一个动态链接(SO)文件
     */
    public interface LibraryStruct extends Library {

        // 加载动态链接库
        // LibraryStruct LIBRARY_STRUCT = Native.load("/program/jna_struct/libhello.so", LibraryStruct.class);

        // Windows环境下,生成dll命令: gcc -shared -o libhello.dll hello.c
        LibraryStruct LIBRARY_STRUCT = Native.load("F:\\JNATestC\\src\\main\\java\\c_jna_2\\libhello.dll", LibraryStruct.class);

        // 定义结构体类,对应结构体Teacher
        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
            }

            @Override
            public String toString() {
                return "TeacherStruct{" +
                        "tea_age=" + tea_age +
                        ", tea_name='" + tea_name + '\'' +
                        '}';
            }

            /**
             * 重写getFieldOrder获取字段列表, 很重要,没有会报错
             */
            @Override
            protected List<String> getFieldOrder() {
                return Arrays.asList("tea_age", "tea_name");
            }
        }

        // 定义结构体类,对应结构体 Student
        class StudentStruct extends Structure {
            //结构体参数类型及顺序要严格按照C结构体的类型及顺序
            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
            }

            @Override
            public String toString() {
                return "TeacherStruct{" +
                        "stu_age=" + stu_age +
                        ", stu_name='" + stu_name + '\'' +
                        '}';
            }

            /**
             * 重写getFieldOrder获取字段列表, 很重要,没有会报错
             */
            @Override
            protected List<String> getFieldOrder() {
                return Arrays.asList("stu_age", "stu_name");
            }
        }
        // 定义一个函数对应C、C++的函数。指针(地址)结构体参数用ByReference,值结构体参数用ByValue
        // 返回直接用值参数就可以
        TeacherStruct.ByValue stuTea(StudentStruct.ByValue stu, TeacherStruct.ByReference tea);
    }

    public static void main(String[] args) {

        LibraryStruct.StudentStruct.ByValue stuByValue = new LibraryStruct.StudentStruct.ByValue();
        LibraryStruct.TeacherStruct.ByReference teaByReference = new LibraryStruct.TeacherStruct.ByReference();

        stuByValue.stu_age = 18;
        stuByValue.stu_name = "小学生";

        teaByReference.tea_age = 48;
        teaByReference.tea_name = "高级教师";

        // 调用函数之前
        System.out.println("调用函数之前teaByReference:" + teaByReference.toString());

        // 调用方法。返回结果
        LibraryStruct.TeacherStruct.ByValue result = LibraryStruct.LIBRARY_STRUCT.stuTea(stuByValue, teaByReference);

        // 查看传地址的teaByReference的值是否变更
        System.out.println("调用函数之后teaByReference:" + teaByReference.toString());

        // 函数返回结果
        System.out.println("调用函数返回结果result.name:" + result.toString());

    }
}

说明:
①还是定义接口LibraryStruct extends Library(跟之前一样)
②在接口内定义结构体类TeacherStruct 和StudentStruct 继承 Structure,这两个分别映射C中的两个结构体
③结构体类内部定义属性用于对应C中结构体的变量(注意类型保持一致)
④在结构体类内部再定义两个静态类ByReference和ByValue 用于表示值传递还是引用传递(用法看main函数)
⑤在结构体类TeacherStruct 和StudentStruct重写getFieldOrder()方法,填入字段

⑥使用时定义结构体变量格式:
Library类.结构体类.ByValue = new Library类.结构体类.ByValue();
Library类.结构体类.ByReference = new Library类.结构体类.ByReference ();
⑦最后调用stuTea方法访问即可。


然后打包成可执行程序jar,运行程序,结果如下
在这里插入图片描述
经检验,结果正确。

四、总结

本文通过验证,实现了JAVA用JNA访问C程序函数,并且使用结构体当作参数传递以及使用结构体变量接收函数返回结果。
过程中有个坑:JNI-[坑3]JNA结构体传参ByValue does not provide enough names
如果还有其他错误很大原因是C程序的问题

上一篇:JNI便捷开发框架JNA框架之引用传递ByReference(三)
开…篇:JNI便捷开发框架JNA框架之入门(一)
.JNI 的:JNI入门与进阶,JNI调用外部非标准程序SO【一】

你可能感兴趣的:(JNI专题,JNA,结构体参数,JNI,JAVA,C/C++)