目录
- 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{}
@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);
} // interface
public 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{}
@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"});
}
}
//函数声明
void showNestedStructWithPointer(CompanyStruct.ByReference cbr);
} // interface
public 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{};
@Override
protected 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{};
@Override
protected 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{};
@Override
protected 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{};
@Override
protected List getFieldOrder(){
return Arrays.asList(new String[] {"area","spot","line"});
}
}
//C函数声明
void get_struct_array_value(IMAGEPOS.ByReference img);
} // interface
public 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{}
@Override
protected 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);
}
运行结果如下
注意:如果没有对结构体数组中第一个元素赋值,会报下面的错
如果对每一项元素都赋相同的初值,会导致获取到的数组元素值都相同。
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];
@Override
protected 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函数最常用,也是比较复杂的用法了。本篇文章内容较长,希望对你有所帮助,不足之处欢迎指出!