欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 美食 > JNA实践之Java模拟C结构体、结构体指针、结构体数组

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

2024/10/25 10:26:58 来源:https://blog.csdn.net/xb_2015/article/details/141498088  浏览:    关键词:JNA实践之Java模拟C结构体、结构体指针、结构体数组

目录

  • 1 JNA模拟C结构体
    • 1.1 结构体本身作参数
    • 1.2 结构体指针作参数
    • 1.3 结构体内部嵌套结构体(结构体本身作参数)
    • 1.4 结构体指针作参数
  • 2 结构体中嵌套结构体数组
    • 2.1 用作输入
    • 2.2 用作输出
  • 3 结构体数组作参数
    • 典型错误1--内存不连续
    • 典型错误2--误用ByValue
  • 4 Java映射C中char[]类型
  • 5 常见问题及其解决方法

第一次写JNA相关的文章是在21年了,中间由于一些事情把后续搁置了,现在补上Java模拟结构体、结构体指针以及结构体中嵌套结构体数组。如果对JNA还不了解,可以先阅读 JNA模拟复杂的C类型一文。

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{}@Overrideprotected 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{}@Overrideprotected 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{};@Overrideprotected List getFieldOrder(){return Arrays.asList(new String[] {"id","name","users","count"});}}//函数声明, 对应C函数void showNestedStruct(CompanyStruct cst);void showNestedStruct(CompanyStruct.ByValue com); } // interfacepublic static void main(String[] args) {CLibrary.UserStruct.ByValue user1 = new CLibrary.UserStruct.ByValue();user1.id = new NativeLong(100001);user1.name = "张三";user1.age = 19;CLibrary.UserStruct.ByValue user2 = new CLibrary.UserStruct.ByValue();user2.id = new NativeLong(100002);user2.name = "关羽";user2.age = 23;CLibrary.UserStruct.ByValue user3 = new CLibrary.UserStruct.ByValue();user3.id = new NativeLong(100003);user3.name = "test";user3.age = 25;CLibrary.CompanyStruct.ByValue cst = new CLibrary.CompanyStruct.ByValue();cst.id = new NativeLong(30001);cst.name = "technology";cst.count = 3;cst.users[0] = user1;cst.users[1] = user2;cst.users[2] = user3;CLibrary.INSTANCE.showNestedStruct(cst); //调用动态库中的函数}
}

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

1.4 结构体指针作参数

使用1.3中的结构体,假设有如下C函数:

extern "C"{void showNestedStructWithPointer(CompanyStruct* cst);
}

函数定义如下

void showNestedStructWithPointer(CompanyStruct *cst){printf("This is nested struct pointer\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{}@Overrideprotected 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{};@Overrideprotected List getFieldOrder(){return Arrays.asList(new String[] {"id","name","users","count"});}}//函数声明void showNestedStructWithPointer(CompanyStruct.ByReference cbr);} // interfacepublic static void main(String[] args) {System.out.println("showNestedStructWithPointer");CLibrary.UserStruct.ByValue user1 = new CLibrary.UserStruct.ByValue();user1.id = new NativeLong(100001);user1.name = "张三";user1.age = 19;CLibrary.UserStruct.ByValue user2 = new CLibrary.UserStruct.ByValue();user2.id = new NativeLong(100002);user2.name = "李四";user2.age = 23;CLibrary.UserStruct.ByValue user3 = new CLibrary.UserStruct.ByValue();user3.id = new NativeLong(100003);user3.name = "test";user3.age = 25;CLibrary.CompanyStruct.ByReference cbr = new CLibrary.CompanyStruct.ByReference();cbr.id = new NativeLong(30001);cbr.name = "TEST";cbr.users[0] = user1;cbr.users[1] = user2;cbr.users[2] = user3;cbr.count = 3;CLibrary.INSTANCE.showNestedStructWithPointer(cbr);}
}

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

2 结构体中嵌套结构体数组

2.1 用作输入

假设有下面的结构体:

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;

如下C函数:

extern "C"{void get_struct_array_value(image_pos*  img_data);
}//函数定义
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);}
}

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 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{};@Overrideprotected List getFieldOrder(){return Arrays.asList(new String[] {"enable","x","y","width","height"});}}public static 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{};@Overrideprotected List getFieldOrder(){return Arrays.asList(new String[] {"enable","x","y"});}}public static 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{};@Overrideprotected List getFieldOrder(){return Arrays.asList(new String[] {"enable","sta_x","sta_y","end_x","end_y"});}}//模拟结构体数组public static 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{};@Overrideprotected List getFieldOrder(){return Arrays.asList(new String[] {"area","spot","line"});}}//C函数声明void get_struct_array_value(IMAGEPOS.ByReference img);} // interfacepublic static void main(String[] args) {CLibrary.AREAPOS.ByValue abv1 = new CLibrary.AREAPOS.ByValue();abv1.enable = 1;abv1.x = 10;abv1.y = 20;abv1.height = 1080;abv1.width = 1920;CLibrary.AREAPOS.ByValue abv2 = new CLibrary.AREAPOS.ByValue();abv2.enable = 0;abv2.x = 20;abv2.y = 10;abv2.height = 1920;abv2.width = 1080;CLibrary.SPOTPOS.ByValue sp1 = new CLibrary.SPOTPOS.ByValue();sp1.enable = 0;sp1.x = 1;sp1.y = 1;CLibrary.SPOTPOS.ByValue sp2 = new CLibrary.SPOTPOS.ByValue();sp2.enable = 1;sp2.x = 2;sp2.y = 2;CLibrary.LINEPOS.ByValue line = new CLibrary.LINEPOS.ByValue();line.enable = 0;line.end_x = 10;line.end_y = 20;line.sta_x = 30;line.sta_y = 40;CLibrary.IMAGEPOS.ByReference img = new CLibrary.IMAGEPOS.ByReference();img.area[0] = abv1;img.area[1] = abv2;img.spot[0] = sp1;img.spot[1] = sp2;img.line = line;CLibrary.INSTANCE.get_struct_array_value(img);}
}

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

2.2 用作输出

结构体中嵌套结构体数组用作输出参数时,需要对结构体数组的第一个元素赋初值。
结构体定义:

typedef struct
{int enable;int max_temp;int max_temp_x;int max_temp_y;int min_temp;	int min_temp_x;int min_temp_y;	int ave_temp;
} area_temp;typedef struct
{int enable;int temp;
} spot_temp;typedef struct
{int enable;int max_temp;int max_temp_x;int max_temp_y;int min_temp;int min_temp_x;int min_temp_y;int ave_temp;
} line_temp;typedef struct
{int max_temp;int max_temp_x;int max_temp_y;int min_temp;int min_temp_x;int min_temp_y;
} globa_temp;typedef struct
{area_temp area[6];spot_temp spot[6];line_temp line;globa_temp globa;
} image_temp;

C函数声明:

extern "C" {int sdk_get_all_temp_data(const char* ip, image_temp* all_data);
}

PS:此处用到的是第三方sdk的so,函数定义就没有了,只做参考用。
area_temp、spot_temp、line_temp、globa_temp结构体参考章节1中的写法,此处只写了复杂的结构体在JNA中的写法,嵌套的结构体数组JNA代码如下:

import com.sun.jna.Structure;
import java.util.Arrays;
import java.util.List;public class ImageTemp extends Structure {public AreaTemp.ByValue[] area = new AreaTemp.ByValue[6];public SpotTemp.ByValue[] spot = new SpotTemp.ByValue[6];public LineTemp.ByValue line = new LineTemp.ByValue();public GlobaTemp.ByValue globa = new GlobaTemp.ByValue();public static class ByReference extends ImageTemp implements Structure.ByReference{}public static class ByValue extends ImageTemp implements Structure.ByValue{}@Overrideprotected List getFieldOrder(){return Arrays.asList("area","spot","line","globa");}
}

测试代码:

@Test
public void sdkGetAllTempDataTest(){A8SDK sdk = new A8SDK();ImageTemp.ByReference itbr = new ImageTemp.ByReference();int size = itbr.size();System.out.println("size="+size);AreaTemp.ByValue atbv = new AreaTemp.ByValue();SpotTemp.ByValue stbv = new SpotTemp.ByValue();itbr.area[0] = atbv;itbr.spot[0] = stbv;int res = sdk.sdkGetAllTempData("",itbr);System.out.println("sdkGetAllTempData res="+res);for(int i = 0; i < 6; i++){System.out.println("GetAllTempData AreaTemp[" + i + "] enable:" + itbr.area[i].enable);System.out.println("GetAllTempData AreaTemp[" + i + "] max_temp:" + itbr.area[i].max_temp);System.out.println("GetAllTempData AreaTemp[" + i + "] max_temp_x:" + itbr.area[i].max_temp_x);System.out.println("GetAllTempData AreaTemp[" + i + "] max_temp_y:" + itbr.area[i].max_temp_y);System.out.println("GetAllTempData AreaTemp[" + i + "] min_temp:" + itbr.area[i].min_temp);System.out.println("GetAllTempData AreaTemp[" + i + "] min_temp_x:" + itbr.area[i].min_temp_x);System.out.println("GetAllTempData AreaTemp[" + i + "] min_temp_y:" + itbr.area[i].min_temp_y);System.out.println("GetAllTempData AreaTemp[" + i + "] ave_temp:" + itbr.area[i].ave_temp);System.out.println("GetAllTempData SpotTemp[" + i + "] enable:" + itbr.spot[i].enable);System.out.println("GetAllTempData SpotTemp[" + i + "] temp:" + itbr.spot[i].temp);}System.out.println("GetAllTempData LineTemp enable:" + itbr.line.enable);System.out.println("GetAllTempData LineTemp max_temp:" + itbr.line.max_temp);System.out.println("GetAllTempData LineTemp max_temp_x:" + itbr.line.max_temp_x);System.out.println("GetAllTempData LineTemp max_temp_y:" + itbr.line.max_temp_y);System.out.println("GetAllTempData LineTemp min_temp:" + itbr.line.min_temp);System.out.println("GetAllTempData LineTemp min_temp_x:" + itbr.line.min_temp_x);System.out.println("GetAllTempData LineTemp min_temp_y:" + itbr.line.min_temp_y);System.out.println("GetAllTempData LineTemp ave_temp:" + itbr.line.ave_temp);System.out.println("GetAllTempData GlobaTemp max_temp:" + itbr.globa.max_temp);System.out.println("GetAllTempData GlobaTemp max_temp_x:" + itbr.globa.max_temp_x);System.out.println("GetAllTempData GlobaTemp max_temp_y:" + itbr.globa.max_temp_y);System.out.println("GetAllTempData GlobaTemp min_temp:" + itbr.globa.min_temp);System.out.println("GetAllTempData GlobaTemp min_temp_x:" + itbr.globa.min_temp_x);System.out.println("GetAllTempData GlobaTemp min_temp_y:" + itbr.globa.min_temp_y);
}

运行结果如下
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/e748dad19459490f9cd64f098e2b7cf1.png
注意:如果没有对结构体数组中第一个元素赋值,会报下面的错
在这里插入图片描述
如果对每一项元素都赋相同的初值,会导致获取到的数组元素值都相同。

3 结构体数组作参数

C函数:

// test.h
struct Person{int age;char name[20];
};
extern "C"{int changeObjs(Person per[], int size);
}//test.cpp
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;
}

JNA代码:

//Person.java
import com.sun.jna.Structure;import java.util.Arrays;
import java.util.List;public class Person extends Structure {public int age;public byte[] name = new byte[20];@Overrideprotected List getFieldOrder(){return Arrays.asList("age","name");}
}

Java接口声明:

int changeObjs(Structure per[], int size);

注意:结构体数组做参数时,Java接口中传该结构体本身即可。加上.ByReference或者.ByValue反而不正确

典型错误1–内存不连续

如果在JNA中调用的C函数参数中有结构体数组时,直接new对象会报错
在这里插入图片描述
结构体数组必须使用连续的内存区域。p1,p2都是new出来的对象,不可能连续,用传统方式初始化数组不能解决。查看JNA api发现提供了toArray

public Structure[] toArray(int size)

Returns a view of this structure’s memory as an array of structures. Note that this Structure must have a public, no-arg constructor. If the structure is currently using a Memory backing, the memory will be resized to fit the entire array.(以结构数组的形式返回此结构的内存视图。请注意,此结构必须具有公共的、无参数的构造函数。如果结构当前使用内存备份,则内存将调整大小以适应整个数组。)
使用JNA的toArray产生内存连续的结构体数组:

Person pb = new Person();
Person[] pers = (Person[]) pb.toArray(2);
pers[0].age = 1;
pers[0].name = Arrays.copyOf("k1".getBytes(), 20);
pers[1].age = 2;
pers[1].name = Arrays.copyOf("k2".getBytes(), 20);
int res = CLibrary.INSTANCE.changeObjs(pers, 2);
System.out.println("res="+res);

在这里插入图片描述

典型错误2–误用ByValue

如果在Java接口声明中错误把参数类型写成Person.ByValue,会报java.lang.IndexOutOfBoundsException

// C函数声明
int changeObjs(Person.ByValue per[], int size);
Person.ByValue pb = new Person.ByValue();
Person.ByValue[] pers = (Person.ByValue[]) pb.toArray(2);
pers[0].age = 1;
pers[0].name = Arrays.copyOf("k1".getBytes(), 20);
pers[1].age = 2;
pers[1].name = Arrays.copyOf("k2".getBytes(), 20);
int res = CLibrary.INSTANCE.changeObjs(pers, 2);

运行报错:
在这里插入图片描述
解决方法:将参数类型改为结构体本身即可,即不带ByReference或ByValue。结构体数组做参数时,要区别于非数组的ByReference和ByValue。

4 Java映射C中char[]类型

C中有时候会用char[]传字符串,java中对应的类型为byte[]。可以借助java.util.Arrays类的copyOf ()方法进行转换。
假设有如下C结构体:

typedef struct {int enable;char ip[20];
} Info;
// C函数声明:
extern "C"{void get_info(Info info);
}
// 函数定义
void get_info(Info info){printf("enable:%d\n",info.enable);printf("ip:%s\n",info.ip);
}

JNA代码:

public static class InfoStruct extends Structure{
public int enable;
public byte[] ip;   // 映射C类型char[]
//public String ip; // 不能直接使用String去映射C类型char[],传参失败public static class ByReference extends InfoStruct implements Structure.ByReference{};
public static class ByValue extends InfoStruct implements Structure.ByValue{};@Override
protected List getFieldOrder(){return Arrays.asList(new String[] {"enable","ip"});}
}

Java调用C函数:
在这里插入图片描述

5 常见问题及其解决方法

参考Java JNA调用C函数常见问题及解决方法一文。
以上就是JNA调用C函数最常用,也是比较复杂的用法了。本篇文章内容较长,希望对你有所帮助,不足之处欢迎指出!

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com