如果不明白如何创建DLL动态链接库项目,可参照文章:https://blog.csdn.net/Xeon_CC/article/details/124873221
提出问题:
C语言程序有个结构体叫BaseGrid,里面有个Grid结构体的成员multi_grid,而且multi_grid是个二维指针数组,意思也就是说相当于java里面的Grid[][]二维数组类型。
请问,Java怎么访问C语言内的BaseGrid内的multi_grid成员变量,即Java如何访问嵌套结构体的成员变量?
在Java,我们会这么做:
BaseGrid baseGrid = new BaseGrid();
for循环,new很多个Grid对象,然后把Grid对象赋值给BaseGrid的multi_grid成员,其中multi_grid成员是装Grid对象的数组。
baseGrid.set(xxx); //做很多的set方法设置成员。
最后我们要访问baseGrid对象里面的multi_grid成员的某个元素Grid类的属性。
我们这么做:
baseGrid.getMultiGrid()[2][3].getW();
但是,如果是Java传递BaseGrid对象给C语言,然后返回baseGrid对象,这时候返回的baseGrid对象就不能如上所说的方法去获取了。
我们既然不能用getter方法获取,也不能用成员变量名称获取,比如obj.name,类似这种访问方式,思路是用地址偏移量的方式去访问成员。
获取成员的方法如下:
创建一个Maven项目,Java代码如下:
package com.example.jna_test01;
import com.sun.jna.*;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.util.HashMap;
@SpringBootApplication
public class JnaTest01Application<T> {
public interface CLibrary extends Library {
JnaTest01Application.CLibrary INSTANCE = (JnaTest01Application.CLibrary) Native.load((Platform.isWindows() ? "msvcrt" : "c"), CLibrary.class);
void printf(String format, Object... args);
}
public interface DllForJava extends Library {
DllForJava INSTANCE = (DllForJava) Native.load("src/main/java/com/example/jna_test01/dll/JNATestDLL.dll", DllForJava.class);
public int getArrSum(Pointer arr, int row, int col);
public BaseGrid getBaseGrid(BaseGrid.ByReference bg_ref, int row, int col);
}
//测试多重指针
public void testMultiPointer() {
CLibrary.INSTANCE.printf("Hello, World\n");
//分配一个24字节的内存,这里准备装入3个指针,一个指针占8个字节
Pointer pointers = new Memory(24);
int pointer_size = 8;
int int_size = 4;
int row = 3;
int col = 5;
for (int i = 0; i < row; i++) {
//分配20字节的内存空间,并且该指针指向这个内存空间的首地址
Pointer pointer = new Memory(20);
for (int j = 0; j < col; j++) {
pointer.setInt(j * int_size, i * j * 2 + 1);
}
pointers.setPointer(i * pointer_size, pointer);
//这里不要释放内存
// Native.free(Pointer.nativeValue(pointer));
}
//调用Dll动态链接库内的getArrSum函数
System.out.println(DllForJava.INSTANCE.getArrSum(pointers, row, col));
System.out.println("释放内存之前,pointers地址:" + pointers);
Native.free(Pointer.nativeValue(pointers));
Pointer.nativeValue(pointers, 0);
System.out.println("释放内存之后,pointers地址:" + pointers);
}
//测试结构体里面有结构体成员
public void testStructInStruct() {
BaseGrid baseGrid = new BaseGrid();
int row = 2;
int col = 3;
BaseGrid bg = DllForJava.INSTANCE.getBaseGrid(baseGrid, row, col);
//https://blog.csdn.net/wankcn/article/details/121209323 C语言的long类型占用几个字节,看操作系统,只有64位的linux系统或者unix占8个字节
//要注意,在c语言char* str是指针,无论你的char数组多长,只占8个字节,因为它只是一个指针
HashMap<String, Integer> fieldMap = new HashMap<>();
fieldMap.put("w", 0);
fieldMap.put("d", 4);
fieldMap.put("db", 8);
fieldMap.put("str", 16);
fieldMap.put("lg", 24);
//每个Grid对象占用几个字节,32个字节,计算是28个字节,但是由于8字节对齐,所以占用32个字节
int gridSize = 32;
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
int w = bg.multi_grid.getPointerArray(0, row)[i].getInt(j * gridSize + fieldMap.get("w"));
int d = bg.multi_grid.getPointerArray(0, row)[i].getInt(j * gridSize + fieldMap.get("d"));
double db = bg.multi_grid.getPointerArray(0, row)[i].getDouble(j * gridSize + fieldMap.get("db"));
String str = byteArrToString(bg.multi_grid.getPointerArray(0, row)[i].getPointer(j * gridSize + fieldMap.get("str")).getByteArray(0, 20));
int lg = bg.multi_grid.getPointerArray(0, row)[i].getInt(j * gridSize + fieldMap.get("lg"));
System.out.println("w = " + w + ", d = " + d + ", db = " + db + ", " + str + ", " + "lg = " + lg);
}
}
}
public String byteArrToString(byte[] bytes) {
String str = "";
for (int i = 0; i < bytes.length; i++) {
str += (char) bytes[i];
}
return str;
}
public static void main(String[] args) {
// SpringApplication.run(DemoApplication.class, args);
JnaTest01Application japp = new JnaTest01Application();
japp.testMultiPointer();
japp.testStructInStruct();
}
}
package com.example.jna_test01;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
@Structure.FieldOrder({"multi_grid", "row", "col"})
public class BaseGrid extends Structure implements Structure.ByReference {
public Pointer multi_grid;
public int row;
public int col;
}
package com.example.jna_test01;
import com.sun.jna.Structure;
import com.sun.jna.WString;
@Structure.FieldOrder({"w", "d"})
public class Grid extends Structure implements Structure.ByReference {
public int w;
public int d;
public double db;
public WString str;
public long lg;
}
DLL动态链接库C语言代码:
#define EXPORT __declspec(dllexport)
#include <iostream>
#include <cmath>
using namespace std;
struct Grid {
int w;
int d;
double db;
char* str;
long lg;
};
struct BaseGrid {
Grid** multi_grid;
int row;
int col;
};
extern "C" {
EXPORT int getArrSum(int** arr, int row, int col) {
/*真正意义上说,i和j都是地址偏移量,并不是说第几个元素,
arr的第一维度是指针arr[0],arr[1]....都是指针,指针占8个字节,
所以,arr[0]访问的是第1个字节的首地址,arr[1]访问的是第9个字节的首地址,也就是每偏移一个单位是8个字节。
然而,arr的第二维存的是整型,每偏移一个单位就是4个字节。
*/
int sum = 0;
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
printf("%d\t", arr[i][j]);
}
printf("\n");
}
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
sum += arr[i][j];
}
}
return sum;
}
EXPORT BaseGrid* getBaseGrid(BaseGrid* baseGrid, int row, int col) {
Grid** gridArrs = new Grid * [row];
for (int i = 0; i < row; i++) {
Grid* grids = new Grid[col];
for (int j = 0; j < col; j++) {
(grids + j)->w = i * j + 3;
(grids + j)->d = i + j + i * j + 20;
(grids + j)->db = i + j + i * j + 40;
(grids + j)->str = new char[20]{ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' };
(grids + j)->lg = 90L;
}
gridArrs[i] = grids;
}
baseGrid->multi_grid = gridArrs;
return baseGrid;
}
}
执行结果