JNA实践之Java模拟C结构体、结构体指针、结构体数组


第一次写JNA相关的文章是在21年了,中间由于一些事情把后续搁置了,现在补上Java模拟结构体、结构体指针以及结构体中嵌套结构体数组。如果对JNA还不了解,可以先阅读 JNA基础之Java映射char*、int*、float*、double*一文。

1 JNA模拟C结构体

        要使用Java类模拟C的结构体,需要Java类继承Structure类。必须注意,继承Structure类的子类中的公共成员的顺序,必须与C语言结构体中变量的顺序保持一致,否则会报错!因为,Java 调用动态链接库中的C 函数,实际上就是一段内存作为函数的参数传递给C函数。动态链接库以为这个参数就是C 语言传过来的参数。同时,C 语言的结构体是一个严格的规范,它定义了内存的次序。因此,JNA 中模拟的结构体的变量顺序绝对不能错。
        如果一个Struct有2个int变量int a, int b,如果JNA中的顺序和C语言中的顺序相反,那么不会报错,但是数据将会被传递到错误的变量中去。
        Structure 类代表了一个原生结构体。当Structure 对象作为一个函数的参数或者返回值传递时,它代表结构体指针。当它被用在另一个结构体内部作为一个字段时,它代表结构体本身。
        另外,Structure 类有两个内部接口Structure.ByReference 和Structure.ByValue。这两个接口仅仅是标记,如果一个类实现Structure.ByReference 接口,就表示这个类代表结构体指针。如果一个类实现Structure.ByValue接口,就表示这个类代表结构体本身。如果不实现这两个接口,那么就相当于你实现了Structure.ByReference接口。使用这两个接口的实现类,可以明确定义我们的Structure 实例表示的是结构体指针还是结构体本身。

1.1 结构体本身作参数

假设有这样一个C语言结构体:

struct User{
   
   
    long id;
    char* name;
    int age;
};

使用上述结构体的函数:

extern "C"{
   
   
void sayUser(User user);
}
void sayUser(User user){
   
   
    printf("id:%ld\n",user.id);
    printf("name:%s\n",user.name);
    printf("age:%d\n",user.age);
}

JNA中可以这样写:

public class Test {
   
   
    public interface CLibrary extends Library {
   
   

        CLibrary INSTANCE = (CLibrary) Native.loadLibrary("/libJNADEMO.so", CLibrary.class);
		
		public static class UserStruct extends Structure{
   
   
            public NativeLong id;
            public String name;
            public int age;
            public static class ByReference extends UserStruct implements Structure.ByReference{
   
   }
            public static class ByValue extends  UserStruct implements Structure.ByValue{
   
   }

            @Override
            protected List getFieldOrder(){
   
   
                return Arrays.asList(new String[] {
   
   "id", "name", "age"});
            }
        }
        void sayUser(UserStruct.ByValue user);
	}
	
	public static void main(String[] args) {
   
   
		CLibrary.UserStruct.ByValue user = new CLibrary.UserStruct.ByValue();
		user.id = new NativeLong(100001);
		user.name = "张三";
		user.age = 29;
		CLibrary.INSTANCE.sayUser(user);
	}
}

运行结果如下:
在这里插入图片描述

1.2 结构体指针作参数

仍然使用1.1中的结构体,C函数修改为下面的:

extern "C"{
   
   
	void sayUser(User* user);
}
void sayUser(User *user){
   
   
    printf("use structure pointer\n");
    printf("id:%ld\n",user->id);
    printf("name:%s\n",user->name);
    printf("age:%d\n",user->age);
}

JNA代码中,修改函数声明为:
void sayUser(UserStruct.ByReference user);
JNA main函数中修改为:

CLibrary.UserStruct.ByReference user = new CLibrary.UserStruct.ByReference();
user.id = new NativeLong(100390301);;
user.name = "test";
user.age = 29;
CLibrary.INSTANCE.sayUser(user);

运行结果如下:
在这里插入图片描述

1.3 结构体内部嵌套结构体(结构体本身作参数)

C语言最复杂的数据类型就是结构体。结构体的内部可以嵌套结构体,这使它可以模拟任何类型的对象。JNA 也可以模拟这类复杂的结构体,结构体内部可以包含结构体对象指针的数组。
假设有如下结构体:

struct User{
   
   
    long id;
    char* name;
    int age;
};

struct CompanyStruct{
   
   
    long id;
    const char* name;
    User users[3];
    int count;
};

如下的C函数:

// 头文件函数声明
extern "C"{
   
   
	void showNestedStruct(CompanyStruct cst); //结构体本身做参数
}
// 函数定义
void showNestedStruct(CompanyStruct cst){
   
   
    printf("This is nested struct\n");
    printf("company id is:%ld\n",cst.id);
    printf("company name:%s\n",cst.name);
    for (int i = 0; i < 3; i++){
   
   
        printf("user[%d] info of company\n",i);
        printf("user id:%ld\n",cst.users[i].id);
        printf("user name:%s\n",cst.users[i].name);
        printf("user age:%d\n",cst.users[i].age);
    }
    printf("count %d\n",cst.count);
}

JNA代码:

import com.sun.jna.*;
import java.util.Arrays;
import java.util.List;

public class Test {
   
   
    public interface CLibrary extends Library {
   
   

        CLibrary INSTANCE = (CLibrary) Native.loadLibrary("/libJNADEMO.so", CLibrary.class);


        public static class UserStruct extends Structure{
   
   
            public NativeLong id;
            public String name;
            public int age;
            public static class ByReference extends UserStruct implements Structure.ByReference{
   
   }
            public static class ByValue extends  UserStruct implements Structure.ByValue{
   
   }

            @Override
            protected List getFieldOrder(){
   
   
                return Arrays.asList(new String[] {
   
   "id","name","age"});
            }
        }

        public static class CompanyStruct extends Structure{
   
   
            public NativeLong id;
            public String name;
            public UserStruct.ByValue[] users = new CLibrary.UserStruct.ByValue[3];
            public int count;
            public static class ByReference extends CompanyStruct implements Structure.ByReference{
   
   };
            public static class ByValue extends CompanyStruct implements Structure.ByValue{
   
   };

            @Override
            protected List getFieldOrder(){
   
   
                return Arrays.asList(new String[] {
   
   "id","name","users","count"});
            }
        }



        //函数声明, 对应C函数void showNestedStruct(CompanyStruct cst);
        void showNestedStruct(CompanyStruct.ByValue com
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值