Java本地方法(JNA)詳解及常見問題
JNA
1、概述
JNA 全稱 Java Native Access,是一個建立在經(jīng)典的 JNI 技術之上的 Java 開源框架。JNA 提供一組 Java 工具類用于在運行期動態(tài)訪問系統(tǒng)本地庫(native library:如 Window 的 dll)而不需要編寫任何 Native/JNI 代碼。開發(fā)人員只要在一個 java 接口中描述目標 native library 的函數(shù)與結構,JNA 將自動實現(xiàn) Java 接口到native function 的映射。
官方網(wǎng)站:https://github.com/java-native-access/jna
Maven依賴:
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>5.10.0</version>
</dependency>
Java Native Access
| Package | Description |
|---|---|
| com.sun.jna | Provides simplified native library access. 提供簡化的本機庫訪問權限。 |
| com.sun.jna.ptr | Provides various native pointer-to-type ( <type> *) representations.提供各種母語指針到類型(<類型> *)表示。 |
| com.sun.jna.win32 | Provides type and function mappers required for standard APIs on the Windows platform. 提供Windows平臺上標準API所需的類型和功能映射器。 |
Platform Utilities
| Package | Description |
|---|---|
| com.sun.jna.platform | Provides cross-platform utilities based on platform-specific libraries. 根據(jù)特定于平臺的庫提供跨平臺實用程序。 |
| com.sun.jna.platform.dnd | Provides integrated, extended drag and drop functionality, allowing ghosted drag images to be used on all platforms. 提供集成的擴展拖放和丟棄功能,允許縮重拖動圖像在所有平臺上使用。 |
Platform Specific
| Package | Description |
|---|---|
| com.sun.jna.platform.linux | Provides common library mappings for Linux. 為Linux提供公共圖書館映射。 |
| com.sun.jna.platform.mac | Provides common library mappings for the OS X platform. 為OS X平臺提供公共庫映射。 |
| com.sun.jna.platform.unix | Provides common library mappings for Unix and X11-based platforms. 為基于UNIX和X11的平臺提供公共圖書館映射。 |
| com.sun.jna.platform.unix.aix | Provides common library mappings for the AIX platform. 為AIX平臺提供公共庫映射。 |
| com.sun.jna.platform.unix.solaris | Provides common library mappings for the Solaris (SunOS) platform. 為Solaris(Sunos)平臺提供公共圖書館映射。 |
| com.sun.jna.platform.win32 | Provides common library mappings for the Windows platform. 為Windows平臺提供公共庫映射。 |
| com.sun.jna.platform.win32.COM | Provides common library mappings for Windows Component Object Model (COM). 為Windows組件對象模型(COM)提供公共庫映射。 |
| com.sun.jna.platform.win32.COM.tlb | Provides common library mappings for COM Type Libraries. 為COM類型庫提供公共庫映射。 |
| com.sun.jna.platform.win32.COM.tlb.imp | Provides common library mappings for COM Type Library implementations. 為COM類型庫實現(xiàn)提供公共庫映射。 |
| com.sun.jna.platform.win32.COM.util | Provides COM Utilities 提供Com Utilities. |
| com.sun.jna.platform.win32.COM.util.annotation | Provides COM Utility annotations 提供com實用程序注釋 |
| com.sun.jna.platform.wince | Provides common library mappings for the Windows CE platform. 為Windows CE平臺提供公共庫映射。 |
Other Packages
| Package | Description |
|---|---|
| com.sun.jna.internal | Provides internal utilities. 提供內(nèi)部實用程序。 |
2、入門案例
2.1、示例一(調(diào)用系統(tǒng)共享庫)
獲取mac平臺下的C共享庫,然后調(diào)用printf函數(shù)打印。
public class HelloWorld {
/**
* 定義一個接口,默認的是繼承Library,如果動態(tài)鏈接庫里額函數(shù)是以stdcall方式輸出的,那么就繼承StdCallLibrary
* 這個接口對應一個動態(tài)連接文件(windows:.dll, linux:.so, mac:.dylib)
*/
public interface CLibrary extends Library {
/**
* 接口內(nèi)部需要一個公共靜態(tài)常量INSTANCE,通過這個常量就可以獲得這個接口的實例,從而使用接口的方法,也就是調(diào)用外部dll/so/dylib的函數(shù)
* 該常量通過Native.load()這個API獲得
* 第一個參數(shù)為共享庫的名稱(不帶后綴)
* 第二個參數(shù)為本接口的Class類型,JNA通過這個這個Class類型,反射創(chuàng)建接口的實例
* 共享庫的查找順序是:
* 先從當前類的當前文件夾找,如果沒找到
* 再從工程當前文件夾下面找,如果找不到
* 最后在當前平臺下面去搜索
*/
CLibrary INSTANCE = Native.load("c", CLibrary.class);
/**
* 接口中只需要定義要用到的函數(shù)或者公共變量,不需要的可以不定義
* z注意參數(shù)和返回值的類型,應該和共享庫中的函數(shù)誒行保持一致
*/
void printf(String format, Object... args);
}
public static void main(String[] args) {
CLibrary.INSTANCE.printf("Hello,World\n");
for (int i = 0; i < args.length; i++) {
CLibrary.INSTANCE.printf("Argument %d:%s\n", i, args[i]);
}
}
}
運行時指定參數(shù)為a b c d,運行結果如下:
Hello,World Argument 0:a Argument 1:b Argument 2:c Argument 3:d
2.2、示例二(調(diào)用自定義共享庫)
自定義一個求和C函數(shù)hello.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
int add(int a, int b)
{
return a + b;
}
編譯為共享庫hello.dylib:
gcc -I $JAVA_HOME/include -I $JAVA_HOME/include/darwin -dynamiclib -o hello.dylib hello.c
使用JNA調(diào)用該共享庫中的add函數(shù):
public class HelloJNA {
public interface LibraryAdd extends Library {
//使用絕對路徑加載
LibraryAdd LIBRARY_ADD = Native.load("/Users/acton_zhang/J2EE/MavenWorkSpace/java_safe_demo/src/main/java/pers/zhang/jna/hello.dylib", LibraryAdd.class);
int add(int a, int b);
}
public static void main(String[] args) {
//調(diào)用映射的接口函數(shù)
int add = LibraryAdd.LIBRARY_ADD.add(10, 15);
System.out.println(add);
}
}
輸出:
25
2、指針參數(shù)Pointer
在JAVA中都是值傳遞,但是因為使用JNA框架,目標函數(shù)是C/C++是有地址變量的,很多時候都需要將變量的結果帶回,因此,地址傳遞在JNA項目中幾乎是必須的。
2.1、使用場景
自定義求和C函數(shù),文件add.c:
//返回a+b的值
//同時c和msg通過指針參數(shù)返回
int add(int a, int b, int *c, char **msg)
{
*c = (a + b) * 2;
char *string = "hello world!";
*msg = string;
return a + b;
}
編譯為共享庫add.dylib:
gcc -I $JAVA_HOME/include -I $JAVA_HOME/include/darwin -dynamiclib -o add.dylib add.c
使用JNA調(diào)用該共享庫中的add函數(shù):
public class AddTest {
public interface LibAdd extends Library {
LibAdd INSTANCE = Native.load("/Users/acton_zhang/J2EE/MavenWorkSpace/java_safe_demo/src/main/java/pers/zhang/jna/add.dylib", LibAdd.class);
int add(int a, int b, int c, String msg);
}
public static void main(String[] args) {
int c = 0;
String msg = "start";
int add = LibAdd.INSTANCE.add(10, 15, c, msg);
System.out.println("求和結果:" + add);
System.out.println("c:" + c);
System.out.println("msg:" + msg);
}
}
結果顯而易見,無論add函數(shù)對c和msg做了何種改變,返回java中,值都不會變更。甚至會因為我們對c和msg賦值導致C函數(shù)訪問到奇怪的地址,導致報錯。
2.2、Pointer類
JNA框架提供了com.sun.jna.Pointer,指針數(shù)據(jù)類型,用于匹配轉換映射函數(shù)的指針變量。
創(chuàng)建Pointer:
//這樣的指針變量定義很像C的寫法,就是在定義的時候申請空間。 Pointer c = new Memory(50); Pointer msg = new Memory(50);
Pointer映射指定的指針類型:
//映射C中的int*類型 //獲取int類型在內(nèi)存中需要的空間大小 int size = Native.getNativeSize(Integer.class); //為Pointer開辟int類型需要的內(nèi)存空間 Pointer int_pointer = new Memory(size); //映射C中的double*類型 Pointer double_pointer = new Memory(Native.getNativeSize(Double.class));
Pointer設置/獲取值:
Pointer的setXxx方法提供了為各種類型設置值的方法:

第一個參數(shù)為偏移量,第二個參數(shù)為值。
//設置int int_pointer.setInt(0, 123); //設置double double_pointer.setDouble(0, 22.33);
Pointer的getXxx方法提供了為各種類型獲取值的方法:

獲取單個值的參數(shù)為偏移量,獲取數(shù)組還需要傳遞一個獲取數(shù)量。
//獲取int int anInt = int_pointer.getInt(0); //獲取double double aDouble = double_pointer.getDouble(0);
釋放Pointer:
Native.free(Pointer.nativeValue(c)); //手動釋放內(nèi)存 Pointer.nativeValue(c, 0); //避免Memory對象被GC時重復執(zhí)行Nativ.free()方法 Native.free(Pointer.nativeValue(msg)); Pointer.nativeValue(msg, 0);
2.3、案例
//返回a+b的值
//同時c和msg通過指針參數(shù)返回
int add(int a, int b, int *c, char **msg)
{
*c = (a + b) * 2;
char *string = "hello world!";
*msg = string;
return a + b;
}
public class AddTest {
public interface LibAdd extends Library {
LibAdd INSTANCE = Native.load("/Users/acton_zhang/J2EE/MavenWorkSpace/java_safe_demo/src/main/java/pers/zhang/jna/add.dylib", LibAdd.class);
int add(int a, int b, Pointer c, Pointer msg);
}
public static void main(String[] args) {
//int類型指針
Pointer c = new Memory(Native.getNativeSize(Integer.class));
//二級指針,所以嵌套Pointer
Pointer msg = new Memory(Native.getNativeSize(Pointer.class));
int add = LibAdd.INSTANCE.add(10, 15, c, msg);
System.out.println("求和結果:" + add);
System.out.println("c:" + c.getInt(0));
//msg實際是二級指針,所以要先獲取一級指針,再獲取值
System.out.println("msg:" + msg.getPointer(0).getString(0));
Native.free(Pointer.nativeValue(c)); //手動釋放內(nèi)存
Pointer.nativeValue(c, 0); //避免Memory對象被GC時重復執(zhí)行Native.free()方法
Native.free(Pointer.nativeValue(msg));
Pointer.nativeValue(msg, 0);
}
}
輸出:
求和結果:25 c:50 msg:hello world!
3、引用對象ByReference
JNA框架提供了com.sun.jna.ptr.ByReference,引用對象類型,提供通用的“指向類型的指針”功能,通常在C代碼中用于向調(diào)用方返回值以及函數(shù)結果。
3.1、使用場景
在低版本的JNA中,如果C中函數(shù)執(zhí)行失敗時,沒有對指針進行處理,那么使用Pointer就會得到一個垃圾值。
C函數(shù)文件test.c:
int test_pointer(int a, int *b)
{
if (a < 0)
{
//未對*b進行處理
return -1;
}
*b = a;
return 0;
}
編譯為共享庫test.dylib:
gcc -I $JAVA_HOME/include -I $JAVA_HOME/include/darwin -dynamiclib -o test.dylib test.c
使用JNA調(diào)用該共享庫中的test_pointer函數(shù):
public class PointerTest {
public interface LibPointerTest extends Library {
LibPointerTest INSTANCE = Native.load("/Users/acton_zhang/J2EE/MavenWorkSpace/java_safe_demo/src/main/java/pers/zhang/jna/test.dylib", LibPointerTest.class);
int test_pointer(int a, Pointer b);
}
public static void main(String[] args) {
int a = -10;
Pointer b = new Memory(Native.getNativeSize(Integer.class));
int add = LibPointerTest.INSTANCE.test_pointer(a, b);
System.out.println(add);
System.out.println(a);
System.out.println(b.getInt(0));
}
}
輸出:
-1 -10 0
本文使用的為5.10版,并未發(fā)現(xiàn)垃圾值的問題。
3.2、ByReference類
ByReference提供了很多繼承類,類似Pointer的指針屬性,每種數(shù)據(jù)類型都對應子類供使用比如int的用IntByReference,字符串的使用PointerByReference。

創(chuàng)建ByReference:
//無參構造,默認值為0 IntByReference intRef = new IntByReference(); //有參構造,根據(jù)指定值創(chuàng)建 IntByReference intRef = new IntByReference(4);
設置/獲取值:
//設置 intRef.setValue(10); //獲取 intRef.getValue();
3.3、案例
int test_pointer(int a, int *b)
{
if (a < 0)
{
//未對*b進行處理
return -1;
}
*b = a;
return 0;
}
public class PointerTest {
public interface LibPointerTest extends Library {
LibPointerTest INSTANCE = Native.load("/Users/acton_zhang/J2EE/MavenWorkSpace/java_safe_demo/src/main/java/pers/zhang/jna/test.dylib", LibPointerTest.class);
int test_pointer(int a, ByReference b);
}
public static void main(String[] args) {
int a = 10;
IntByReference b = new IntByReference();
int add = LibPointerTest.INSTANCE.test_pointer(10, b);
System.out.println(add);
System.out.println(a);
System.out.println(b.getValue());
}
}
輸出:
0 10 10
3.4、Pointer與ByReference對比
- Pointer和ByReference都可以在JNA項目中用來地址傳遞參數(shù)
- Pointer使用方式類似C/C++需要手動分配/回收內(nèi)存
- ByReference使用方式就是Java語法,內(nèi)存通過垃圾回收機制自動完成
總的來講ByReference更加方便,但是對于多層的指針引用,可能Pointer更合適處理嵌套結構。
4、Java模擬C結構體
4.1、使用場景
結構體作為參數(shù)有兩個點,一個是傳入,一個是返回。傳入的還分傳值和傳引用。
目標C程序,struct.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// teacher 結構體定義
typedef struct {
int tea_age;
char *tea_name;
} Teacher;
// student 結構體定義
typedef struct {
int stu_age;
char *stu_name;
} Student;
Teacher stuTea(Student stu, Teacher *tea) {
printf("stu-name=%s/n", stu.stu_name);
printf("stu-age=%d/n", stu.stu_age);
// 將stu復制給tea做函數(shù)返回
Teacher teacher;
teacher.tea_name = stu.stu_name;
teacher.tea_age = stu.stu_age;
tea->tea_name = strcat(tea->tea_name, "是好老師");
return teacher;
}
函數(shù)內(nèi)部的功能也簡單:
- 打印stu的內(nèi)容【驗證值傳遞是否正確】
- 把stu的內(nèi)容復制給結果變量teacher,用于函數(shù)返回【驗證是否能返回結構體】
- 改變傳入結構體變量tea的值【驗證結構體引用傳遞是否生效】
編譯為共享庫struct.dylib:
gcc -I $JAVA_HOME/include -I $JAVA_HOME/include/darwin -dynamiclib -o struct.dylib struct.c
JNA調(diào)用:
public class StructTest {
//定義一個接口,描述本地庫
public interface LibStruct extends Library {
LibStruct INSTANCE = Native.load("/Users/acton_zhang/J2EE/MavenWorkSpace/java_safe_demo/src/main/java/pers/zhang/jna/struct.dylib", LibStruct.class);
//定義結構體
class TeacherStruct extends Structure {
//結構體參數(shù)類型及順序要嚴格按照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 {
//拷貝參數(shù)傳遞使用ByValue
}
//重寫getFieldOrder獲取字段列表, 很重要,沒有會報錯
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[]{"tea_age", "tea_name"});
}
@Override
public String toString() {
return "TeacherStruct{" +
"tea_age=" + tea_age +
", tea_name='" + tea_name + '\'' +
"} " + super.toString();
}
}
//定義結構體
class StudentStruct extends Structure {
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 {
//拷貝參數(shù)傳遞使用ByValue
}
//重寫getFieldOrder獲取字段列表, 很重要,沒有會報錯
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[]{"stu_age", "stu_name"});
}
@Override
public String toString() {
return "StudentStruct{" +
"stu_age=" + stu_age +
", stu_name='" + stu_name + '\'' +
"} " + super.toString();
}
}
//描述本地函數(shù)
TeacherStruct.ByValue stuTea(StudentStruct.ByValue stu, TeacherStruct.ByReference tea);
}
public static void main(String[] args) {
LibStruct.StudentStruct.ByValue stuByValue = new LibStruct.StudentStruct.ByValue();
LibStruct.TeacherStruct.ByReference teaByReference = new LibStruct.TeacherStruct.ByReference();
stuByValue.stu_age = 18;
stuByValue.stu_name = "小學生";
teaByReference.tea_age = 48;
teaByReference.tea_name = "高級教師";
// 調(diào)用函數(shù)之前
System.out.println("調(diào)用函數(shù)之前teaByReference:" + teaByReference.toString());
// 調(diào)用方法。返回結果
LibStruct.TeacherStruct.ByValue result = LibStruct.INSTANCE.stuTea(stuByValue, teaByReference);
// 查看傳地址的teaByReference的值是否變更
System.out.println("調(diào)用函數(shù)之后teaByReference:" + teaByReference.toString());
// 函數(shù)返回結果
System.out.println("調(diào)用函數(shù)返回結果result.name:" + result.toString());
}
}
輸出:
調(diào)用函數(shù)之前teaByReference:TeacherStruct{tea_age=48, tea_name='高級教師'} StructTest$LibStruct$TeacherStruct$ByReference(auto-allocated@0x7ffc36203c40 (16 bytes)) {
int tea_age@0x0=0x0030
String tea_name@0x8=高級教師
}
調(diào)用函數(shù)之后teaByReference:TeacherStruct{tea_age=48, tea_name='高級教師是好老師'} StructTest$LibStruct$TeacherStruct$ByReference(auto-allocated@0x7ffc36203c40 (16 bytes)) {
int tea_age@0x0=0x0030
String tea_name@0x8=高級教師是好老師
}
調(diào)用函數(shù)返回結果result.name:TeacherStruct{tea_age=18, tea_name='小學生'} StructTest$LibStruct$TeacherStruct$ByValue(auto-allocated@0x7ffc3620b480 (16 bytes)) {
int tea_age@0x0=0x0012
String tea_name@0x8=小學生
}
stu-name=小學生/nstu-age=18/n
4.2、Structure類
要使用 Java 類模擬 C 的結構體,需要 Java 類繼承Structure類。
必須要注意,Structure子類中的公共字段的順序,必須與 C 語言中的結構的順序保持一致,否則會報錯!因為,Java 調(diào)用動態(tài)鏈接庫中的 C 函數(shù),實際上就是一段內(nèi)存作為函數(shù)的參數(shù)傳遞給 C 函數(shù)。動態(tài)鏈接庫以為這個參數(shù)就是 C 語言傳過來的參數(shù)。同時,C 語言的結構體是一個嚴格的規(guī)范,它定義了內(nèi)存的次序。因此,JNA 中模擬的結構體的變量順序絕對不能錯。
如果一個 Struct 有 2 個 int 變量 int a, int b,如果 JNA 中的次序和 C 語言中的次序相反,那么不會報錯,但是數(shù)據(jù)將會被傳遞到錯誤的字段中去。
另外,Structure類有兩個內(nèi)部接口Structure.ByReference和Structure.ByValue。
這兩個接口僅僅是標記:
- 如果一個類實現(xiàn) Structure.ByReference 接口,就表示這個類代表結構體指針。
- 如果一個類實現(xiàn) Structure.ByValue 接口,就表示這個類代表結構體本身。
- 如果不實現(xiàn)這兩個接口,那么就相當于你實現(xiàn)了 Structure.ByReference 接口。
使用這兩個接口的實現(xiàn)類,可以明確定義我們的 Structure 實例表示的是結構體指針還是結構體本身。
4.3、結構體本身作為參數(shù)
struct_self_param.c
#include <stdio.h>
struct User {
long id;
char* name;
int age;
};
void sayUser(struct User user)
{
printf("id:%ld\n", user.id);
printf("name:%s\n", user.name);
printf("age:%d\n", user.age);
}
編譯為共享庫struct_self_param.dylib:
gcc -I $JAVA_HOME/include -I $JAVA_HOME/include/darwin -dynamiclib -o struct_self_param.dylib struct_self_param.c
JNA調(diào)用:
public class StructSelfParamTest {
//描述本地共享庫
public interface LibStructSelfParam extends Library {
LibStructSelfParam INSTANCE = Native.load("/Users/acton_zhang/J2EE/MavenWorkSpace/java_safe_demo/src/main/java/pers/zhang/jna/struct_self_param.dylib", LibStructSelfParam.class);
//定義結構體
class UserStruct extends Structure {
//公共字段的順序,必須與C語言中的結構的順序保持一致
public NativeLong id;
public String name;
public int age;
// 定義值傳遞和指針傳遞類
public static class ByReference extends UserStruct implements Structure.ByReference {
//指針和引用的傳遞使用ByReference
}
public static class ByValue extends UserStruct implements Structure.ByValue {
//拷貝參數(shù)傳遞使用ByValue
}
// 重寫getFieldOrder獲取字段列表, 很重要,沒有會報錯
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[]{"id", "name", "age"});
}
}
//描述本地函數(shù),值傳遞
void sayUser(UserStruct.ByValue user);
}
public static void main(String[] args) {
LibStructSelfParam.UserStruct.ByValue user = new LibStructSelfParam.UserStruct.ByValue();
user.id = new NativeLong(10000);
user.name = "tom";
user.age = 18;
LibStructSelfParam.INSTANCE.sayUser(user);
}
}
輸出:
id:10000 name:tom age:18
4.3、結構體指針作為參數(shù)
struct_pointer_param.c
#include <stdio.h>
struct User {
long id;
char* name;
int age;
};
void sayUser(struct User* user)
{
printf("use strcture pointer\n");
printf("id:%ld\n", user->id);
printf("name:%s\n", user->name);
printf("age:%d\n", user->age);
}編譯為共享庫struct_pointer_param.dylib:
gcc -I $JAVA_HOME/include -I $JAVA_HOME/include/darwin -dynamiclib -o struct_pointer_param.dylib struct_pointer_param.c
JNA調(diào)用:
public class StructPointerParamTest {
//描述本地共享庫
public interface LibStructPointerParam extends Library {
LibStructPointerParam INSTANCE = Native.load("/Users/acton_zhang/J2EE/MavenWorkSpace/java_safe_demo/src/main/java/pers/zhang/jna/struct_pointer_param.dylib", LibStructPointerParam.class);
//定義結構體
class UserStruct extends Structure {
//公共字段的順序,必須與C語言中的結構的順序保持一致
public NativeLong id;
public String name;
public int age;
// 定義值傳遞和指針傳遞類
public static class ByReference extends StructSelfParamTest.LibStructSelfParam.UserStruct implements Structure.ByReference {
//指針和引用的傳遞使用ByReference
}
public static class ByValue extends StructSelfParamTest.LibStructSelfParam.UserStruct implements Structure.ByValue {
//拷貝參數(shù)傳遞使用ByValue
}
// 重寫getFieldOrder獲取字段列表, 很重要,沒有會報錯
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[]{"id", "name", "age"});
}
}
//描述本地函數(shù),指針傳遞
void sayUser(UserStruct.ByReference user);
}
public static void main(String[] args) {
LibStructPointerParam.UserStruct.ByReference user = new LibStructPointerParam.UserStruct.ByReference();
user.id = new NativeLong(10000);
user.name = "tom";
user.age = 18;
LibStructPointerParam.INSTANCE.sayUser(user);
}
}
輸出:
use strcture pointer id:10000 name:tom age:18
4.4、嵌套結構體本身作為參數(shù)
C 語言最復雜的數(shù)據(jù)類型就是結構體。結構體的內(nèi)部可以嵌套結構體,這使它可以模擬任何類型的對象。JNA 也可以模擬這類復雜的結構體,結構體內(nèi)部可以包含結構體對象指針的數(shù)組。
struct_nested_param.c
#include <stdio.h>
struct User {
long id;
char* name;
int age;
};
struct Company {
long id;
const char* name;
struct User users[3];
int count;
};
void showNestedStruct(struct Company company)
{
printf("This is nested struct.\n");
printf("company id is:%ld\n", company.id);
printf("company name is:%s\n", company.name);
for (int i =0; i < 3; i++)
{
printf("user[%d] info of company\n", i);
printf("user id:%ld\n", company.users[i].id);
printf("user name:%s\n", company.users[i].name);
printf("user age:%d\n", company.users[i].age);
}
printf("count %d\n", company.count);
}
編譯為共享庫struct_nested_param.dylib:
gcc -I $JAVA_HOME/include -I $JAVA_HOME/include/darwin -dynamiclib -o struct_nested_param.dylib struct_nested_param.c
JNA調(diào)用:
public class StructNestedParamTest {
//描述本地共享庫
public interface LibStructNestedParam extends Library {
LibStructNestedParam INSTANCE = Native.load("/Users/acton_zhang/J2EE/MavenWorkSpace/java_safe_demo/src/main/java/pers/zhang/jna/struct_nested_param.dylib", LibStructNestedParam.class);
//定義User結構體
class UserStruct extends Structure {
//公共字段的順序,必須與C語言中的結構的順序保持一致
public NativeLong id;
public String name;
public int age;
// 定義值傳遞和指針傳遞類
public static class ByReference extends UserStruct implements Structure.ByReference {
//指針和引用的傳遞使用ByReference
}
public static class ByValue extends UserStruct implements Structure.ByValue {
//拷貝參數(shù)傳遞使用ByValue
}
// 重寫getFieldOrder獲取字段列表, 很重要,沒有會報錯
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[]{"id", "name", "age"});
}
}
//定義Company結構體
class CompanyStruct extends Structure {
//公共字段的順序,必須與C語言中的結構的順序保持一致
public NativeLong id;
public String name;
public UserStruct.ByValue[] users = new UserStruct.ByValue[3];
public int count;
// 定義值傳遞和指針傳遞類
public static class ByReference extends CompanyStruct implements Structure.ByReference {
//指針和引用的傳遞使用ByReference
}
public static class ByValue extends CompanyStruct implements Structure.ByValue {
//拷貝參數(shù)傳遞使用ByValue
}
// 重寫getFieldOrder獲取字段列表, 很重要,沒有會報錯
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[]{"id", "name", "users", "count"});
}
}
//描述本地函數(shù),值傳遞
void showNestedStruct(CompanyStruct.ByValue company);
}
public static void main(String[] args) {
LibStructNestedParam.UserStruct.ByValue user1 = new LibStructNestedParam.UserStruct.ByValue();
user1.id = new NativeLong(1);
user1.name = "zhangsan";
user1.age = 18;
LibStructNestedParam.UserStruct.ByValue user2 = new LibStructNestedParam.UserStruct.ByValue();
user2.id = new NativeLong(2);
user2.name = "lisi";
user2.age = 19;
LibStructNestedParam.UserStruct.ByValue user3 = new LibStructNestedParam.UserStruct.ByValue();
user3.id = new NativeLong(3);
user3.name = "wangwu";
user3.age = 20;
LibStructNestedParam.CompanyStruct.ByValue company = new LibStructNestedParam.CompanyStruct.ByValue();
company.id = new NativeLong(1000001);
company.name = "XXXXXX有限責任公司";
company.count = 3;
company.users[0] = user1;
company.users[1] = user2;
company.users[2] = user3;
LibStructNestedParam.INSTANCE.showNestedStruct(company);
}
}
輸出:
This is nested struct. company id is:1000001 company name is:XXXXXX有限責任公司 user[0] info of company user id:1 user name:zhangsan user age:18 user[1] info of company user id:2 user name:lisi user age:19 user[2] info of company user id:3 user name:wangwu user age:20 count 3
4.5、嵌套結構體指針作為參數(shù)
struct_nested_pointer_param.c
#include <stdio.h>
struct User {
long id;
char* name;
int age;
};
struct Company {
long id;
const char* name;
struct User users[3];
int count;
};
void showNestedStruct(struct Company* company)
{
printf("This is nested struct.\n");
printf("company id is:%ld\n", company->id);
printf("company name is:%s\n", company->name);
for (int i =0; i < 3; i++)
{
printf("user[%d] info of company\n", i);
printf("user id:%ld\n", company->users[i].id);
printf("user name:%s\n", company->users[i].name);
printf("user age:%d\n", company->users[i].age);
}
printf("count %d\n", company->count);
}
編譯為共享庫struct_nested_pointer_param.dylib:
gcc -I $JAVA_HOME/include -I $JAVA_HOME/include/darwin -dynamiclib -o struct_nested_pointer_param.dylib struct_nested_pointer_param.c
JNA調(diào)用:
public class StructNestedPointerParamTest {
//描述本地共享庫
public interface LibStructNestedPointerParam extends Library {
LibStructNestedPointerParam INSTANCE = Native.load("/Users/acton_zhang/J2EE/MavenWorkSpace/java_safe_demo/src/main/java/pers/zhang/jna/struct_nested_pointer_param.dylib", LibStructNestedPointerParam.class);
//定義User結構體
class UserStruct extends Structure {
//公共字段的順序,必須與C語言中的結構的順序保持一致
public NativeLong id;
public String name;
public int age;
// 定義值傳遞和指針傳遞類
public static class ByReference extends UserStruct implements Structure.ByReference {
//指針和引用的傳遞使用ByReference
}
public static class ByValue extends UserStruct implements Structure.ByValue {
//拷貝參數(shù)傳遞使用ByValue
}
// 重寫getFieldOrder獲取字段列表, 很重要,沒有會報錯
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[]{"id", "name", "age"});
}
}
//定義Company結構體
class CompanyStruct extends Structure {
//公共字段的順序,必須與C語言中的結構的順序保持一致
public NativeLong id;
public String name;
public UserStruct.ByValue[] users = new UserStruct.ByValue[3];
public int count;
// 定義值傳遞和指針傳遞類
public static class ByReference extends CompanyStruct implements Structure.ByReference {
//指針和引用的傳遞使用ByReference
}
public static class ByValue extends CompanyStruct implements Structure.ByValue {
//拷貝參數(shù)傳遞使用ByValue
}
// 重寫getFieldOrder獲取字段列表, 很重要,沒有會報錯
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[]{"id", "name", "users", "count"});
}
}
//描述本地函數(shù),指針傳遞
void showNestedStruct(CompanyStruct.ByReference company);
}
public static void main(String[] args) {
LibStructNestedPointerParam.UserStruct.ByValue user1 = new LibStructNestedPointerParam.UserStruct.ByValue();
user1.id = new NativeLong(1);
user1.name = "zhangsan";
user1.age = 18;
LibStructNestedPointerParam.UserStruct.ByValue user2 = new LibStructNestedPointerParam.UserStruct.ByValue();
user2.id = new NativeLong(2);
user2.name = "lisi";
user2.age = 19;
LibStructNestedPointerParam.UserStruct.ByValue user3 = new LibStructNestedPointerParam.UserStruct.ByValue();
user3.id = new NativeLong(3);
user3.name = "wangwu";
user3.age = 20;
LibStructNestedPointerParam.CompanyStruct.ByReference company = new LibStructNestedPointerParam.CompanyStruct.ByReference();
company.id = new NativeLong(1000001);
company.name = "XXXXXX有限責任公司";
company.count = 3;
company.users[0] = user1;
company.users[1] = user2;
company.users[2] = user3;
LibStructNestedPointerParam.INSTANCE.showNestedStruct(company);
}
}
輸出:
This is nested struct. company id is:1000001 company name is:XXXXXX有限責任公司 user[0] info of company user id:1 user name:zhangsan user age:18 user[1] info of company user id:2 user name:lisi user age:19 user[2] info of company user id:3 user name:wangwu user age:20 count 3
4.6、結構體中嵌套結構體數(shù)組
1)、作為輸入?yún)?shù)
struct_nested_array_param.c
#include <stdio.h>
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;
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);
}
}
編譯為共享庫struct_nested_array_param.dylib:
gcc -I $JAVA_HOME/include -I $JAVA_HOME/include/darwin -dynamiclib -o struct_nested_array_param.dylib struct_nested_array_param.c
JNA調(diào)用:
public class StructNestedArrayParamTest {
public interface LibStructNestedArrayParam extends Library {
LibStructNestedArrayParam INSTANCE = Native.load("/Users/acton_zhang/J2EE/MavenWorkSpace/java_safe_demo/src/main/java/pers/zhang/jna/struct_nested_array_param.dylib", LibStructNestedArrayParam.class);
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<String> getFieldOrder() {
return Arrays.asList(new String[]{"enable", "x", "y", "width", "height"});
}
}
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<String> getFieldOrder() {
return Arrays.asList(new String[]{"enable", "x", "y"});
}
}
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<String> getFieldOrder() {
return Arrays.asList(new String[]{"enable", "sta_x", "sta_y", "end_x", "end_y"});
}
}
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<String> getFieldOrder() {
return Arrays.asList(new String[]{"area", "spot", "line"});
}
}
void get_struct_array_value(ImagePos.ByReference img);
}
public static void main(String[] args) {
LibStructNestedArrayParam.AreaPos.ByValue a1 = new LibStructNestedArrayParam.AreaPos.ByValue();
a1.enable = 1;
a1.x = 10;
a1.y = 20;
a1.height = 1080;
a1.width = 1920;
LibStructNestedArrayParam.AreaPos.ByValue a2 = new LibStructNestedArrayParam.AreaPos.ByValue();
a1.enable = 0;
a1.x = 20;
a1.y = 10;
a1.height = 1920;
a1.width = 1080;
LibStructNestedArrayParam.SpotPos.ByValue s1 = new LibStructNestedArrayParam.SpotPos.ByValue();
s1.enable = 0;
s1.x = 1;
s1.y = 1;
LibStructNestedArrayParam.SpotPos.ByValue s2 = new LibStructNestedArrayParam.SpotPos.ByValue();
s1.enable = 1;
s1.x = 2;
s1.y = 2;
LibStructNestedArrayParam.LinePos.ByValue line = new LibStructNestedArrayParam.LinePos.ByValue();
line.enable = 0;
line.end_x = 10;
line.end_y = 20;
line.sta_x = 30;
line.sta_y = 40;
LibStructNestedArrayParam.ImagePos.ByReference img = new LibStructNestedArrayParam.ImagePos.ByReference();
img.area[0] = a1;
img.area[1] = a2;
img.spot[0] = s1;
img.spot[1] = s2;
img.line = line;
LibStructNestedArrayParam.INSTANCE.get_struct_array_value(img);
}
}
輸出:
line_pos enable:0 line_pos sta_x:30 line_pos sta_y:40 line_pos end_x:10 line_pos end_y:20 area_pos[0] enable:0 area_pos[0] x:20 area_pos[0] y:10 area_pos[0] width:1080 area_pos[0] height:1920 area_pos[1] enable:0 area_pos[1] x:0 area_pos[1] y:0 area_pos[1] width:0 area_pos[1] height:0 spot_pos[0] enable:1 spot_pos[0] x:2 spot_pos[0] y:2 spot_pos[1] enable:0 spot_pos[1] x:0 spot_pos[1] y:0
2)、作為輸出參數(shù)
結構體中嵌套結構體數(shù)組用作輸出參數(shù)時,需要對結構體數(shù)組的第一個元素賦初值。
struct_nested_array_out.c
#include <stdio.h>
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;
void set_struct_array_value(image_pos *img_data){
area_pos a1,a2;
a1.enable = 1;
a1.x = 10;
a1.y = 20;
a1.height = 1090;
a1.width = 1920;
a2.enable = 0;
a2.x = 20;
a2.y = 10;
a2.height = 1920;
a2.width = 1080;
spot_pos s1,s2;
s1.enable = 0;
s1.x = 1;
s1.y = 1;
s2.enable = 1;
s2.x = 2;
s2.y = 2;
line_pos l;
l.enable = 0;
l.end_x = 10;
l.end_y = 20;
l.sta_x = 30;
l.sta_y = 40;
img_data->area[0] = a1;
img_data->area[1] = a2;
img_data->spot[0] = s1;
img_data->spot[1] = s2;
img_data->line = l;
}
編譯為共享庫struct_nested_array_out.dylib:
gcc -I $JAVA_HOME/include -I $JAVA_HOME/include/darwin -dynamiclib -o struct_nested_array_out.dylib struct_nested_array_out.c
JNA調(diào)用:
public class StructNestedArrayOutTest {
public interface LibStructNestedArrayOut extends Library {
LibStructNestedArrayOut INSTANCE = Native.load("/Users/acton_zhang/J2EE/MavenWorkSpace/java_safe_demo/src/main/java/pers/zhang/jna/struct_nested_array_out.dylib", LibStructNestedArrayOut.class);
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<String> getFieldOrder() {
return Arrays.asList(new String[]{"enable", "x", "y", "width", "height"});
}
@Override
public String toString() {
return "AreaPos{" +
"enable=" + enable +
", x=" + x +
", y=" + y +
", width=" + width +
", height=" + height +
"} " + super.toString();
}
}
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<String> getFieldOrder() {
return Arrays.asList(new String[]{"enable", "x", "y"});
}
@Override
public String toString() {
return "SpotPos{" +
"enable=" + enable +
", x=" + x +
", y=" + y +
"} " + super.toString();
}
}
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<String> getFieldOrder() {
return Arrays.asList(new String[]{"enable", "sta_x", "sta_y", "end_x", "end_y"});
}
@Override
public String toString() {
return "LinePos{" +
"enable=" + enable +
", sta_x=" + sta_x +
", sta_y=" + sta_y +
", end_x=" + end_x +
", end_y=" + end_y +
"} " + super.toString();
}
}
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<String> getFieldOrder() {
return Arrays.asList(new String[]{"area", "spot", "line"});
}
}
void set_struct_array_value(ImagePos.ByReference img);
}
public static void main(String[] args) {
LibStructNestedArrayOut.ImagePos.ByReference img = new LibStructNestedArrayOut.ImagePos.ByReference();
// img.area[0] = new LibStructNestedArrayOut.AreaPos.ByValue();
// img.spot[0] = new LibStructNestedArrayOut.SpotPos.ByValue();
LibStructNestedArrayOut.INSTANCE.set_struct_array_value(img);
for (int i = 0; i < 2; i++)
System.out.println(img.area[i]);
for (int i = 0; i < 2; i++)
System.out.println(img.spot[i]);
System.out.println(img.line);
}
}
如果未對數(shù)組第一項賦值,會報java.lang.IndexOutOfBoundsException
Exception in thread "main" java.lang.IndexOutOfBoundsException: Bounds exceeds available space : size=20, offset=40
所以一定要對輸出參數(shù)中的數(shù)組第一項賦值:
public static void main(String[] args) {
LibStructNestedArrayOut.ImagePos.ByReference img = new LibStructNestedArrayOut.ImagePos.ByReference();
img.area[0] = new LibStructNestedArrayOut.AreaPos.ByValue();
img.spot[0] = new LibStructNestedArrayOut.SpotPos.ByValue();
LibStructNestedArrayOut.INSTANCE.set_struct_array_value(img);
for (int i = 0; i < 2; i++)
System.out.println(img.area[i]);
for (int i = 0; i < 2; i++)
System.out.println(img.spot[i]);
System.out.println(img.line);
}
輸出:
AreaPos{enable=1, x=10, y=20, width=1920, height=1090} StructNestedArrayOutTest$LibStructNestedArrayOut$AreaPos$ByValue(allocated@0x7fb3b0d1bd10 (20 bytes) (shared from auto-allocated@0x7fb3b0d1bd10 (84 bytes))) {
int enable@0x0=0x0001
int x@0x4=0x000A
int y@0x8=0x0014
int width@0xC=0x0780
int height@0x10=0x0442
}
AreaPos{enable=0, x=20, y=10, width=1080, height=1920} StructNestedArrayOutTest$LibStructNestedArrayOut$AreaPos$ByValue(allocated@0x7fb3b0d1bd24 (20 bytes) (shared from auto-allocated@0x7fb3b0d1bd10 (84 bytes))) {
int enable@0x0=0x0000
int x@0x4=0x0014
int y@0x8=0x000A
int width@0xC=0x0438
int height@0x10=0x0780
}
SpotPos{enable=0, x=1, y=1} StructNestedArrayOutTest$LibStructNestedArrayOut$SpotPos$ByValue(allocated@0x7fb3b0d1bd38 (12 bytes) (shared from auto-allocated@0x7fb3b0d1bd10 (84 bytes))) {
int enable@0x0=0x0000
int x@0x4=0x0001
int y@0x8=0x0001
}
SpotPos{enable=1, x=2, y=2} StructNestedArrayOutTest$LibStructNestedArrayOut$SpotPos$ByValue(allocated@0x7fb3b0d1bd44 (12 bytes) (shared from auto-allocated@0x7fb3b0d1bd10 (84 bytes))) {
int enable@0x0=0x0001
int x@0x4=0x0002
int y@0x8=0x0002
}
LinePos{enable=0, sta_x=30, sta_y=40, end_x=10, end_y=20} StructNestedArrayOutTest$LibStructNestedArrayOut$LinePos$ByValue(allocated@0x7fb3b0d1bd50 (20 bytes) (shared from auto-allocated@0x7fb3b0d1bd10 (84 bytes))) {
int enable@0x0=0x0000
int sta_x@0x4=0x001E
int sta_y@0x8=0x0028
int end_x@0xC=0x000A
int end_y@0x10=0x0014
}
4.7、結構體數(shù)組作為參數(shù)
文件struct_array_param.c
#include <stdio.h>
#include <string.h>
typedef struct {
int age;
char name[20];
} Person;
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;
}
編譯為共享庫struct_array_param.dylib:
gcc -I $JAVA_HOME/include -I $JAVA_HOME/include/darwin -dynamiclib -o struct_array_param.dylib struct_array_param.c
1)、錯誤的調(diào)用一
public class StructArrayParamTest {
public interface LibStructArrayParam extends Library {
LibStructArrayParam INSTANCE = Native.load("/Users/acton_zhang/J2EE/MavenWorkSpace/java_safe_demo/src/main/java/pers/zhang/jna/struct_array_param.dylib", LibStructArrayParam.class);
class Person extends Structure {
public int age;
public byte[] name = new byte[20];
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[]{"age", "name"});
}
//值傳遞
public static class ByValue extends Person implements Structure.ByValue { }
}
//描述函數(shù)
int changeObjs(Person.ByValue[] per, int size);
}
public static void main(String[] args) {
LibStructArrayParam.Person.ByValue[] per = new LibStructArrayParam.Person.ByValue[2];
LibStructArrayParam.Person.ByValue p1 = new LibStructArrayParam.Person.ByValue();
LibStructArrayParam.Person.ByValue p2 = new LibStructArrayParam.Person.ByValue();
p1.age = 1;
p1.name = Arrays.copyOf("tom".getBytes(), 20);
p2.age = 2;
p2.name = Arrays.copyOf("jerry".getBytes(), 20);
per[0] = p1;
per[1] = p2;
LibStructArrayParam.INSTANCE.changeObjs(per, 2);
}
}
如果在 Java 接口聲明中錯誤把參數(shù)類型寫成Person.ByValue,會報java.lang.IndexOutOfBoundsException。將參數(shù)類型改為結構體本身即可,即不帶 ByReference 或 ByValue。結構體數(shù)組做參數(shù)時, 要區(qū)別于非數(shù)組的 ByReference 和 ByValue。
2)、錯誤的調(diào)用二
public class StructArrayParamTest {
public interface LibStructArrayParam extends Library {
LibStructArrayParam INSTANCE = Native.load("/Users/acton_zhang/J2EE/MavenWorkSpace/java_safe_demo/src/main/java/pers/zhang/jna/struct_array_param.dylib", LibStructArrayParam.class);
class Person extends Structure {
public int age;
public byte[] name = new byte[20];
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[]{"age", "name"});
}
}
//描述函數(shù)
int changeObjs(Person[] per, int size);
}
public static void main(String[] args) {
LibStructArrayParam.Person[] per = new LibStructArrayParam.Person[2];
LibStructArrayParam.Person p1 = new LibStructArrayParam.Person();
LibStructArrayParam.Person p2 = new LibStructArrayParam.Person();
p1.age = 1;
p1.name = Arrays.copyOf("tom".getBytes(), 20);
p2.age = 2;
p2.name = Arrays.copyOf("jerry".getBytes(), 20);
per[0] = p1;
per[1] = p2;
LibStructArrayParam.INSTANCE.changeObjs(per, 2);
}
}
會報錯Exception in thread "main" java.lang.IllegalArgumentException: Structure array elements must use contiguous memory (bad backing address at Structure array index 1)
結構體數(shù)組必須使用連續(xù)的內(nèi)存區(qū)域。p1,p2 都是 new 出來的對象,不可能連續(xù),用傳統(tǒng)方式初始化數(shù)組不能解決。
應使用JNA提供的toArray方法:
public Structure[] toArray(int size);
3)、正確的調(diào)用
public class StructArrayParamTest {
public interface LibStructArrayParam extends Library {
LibStructArrayParam INSTANCE = Native.load("/Users/acton_zhang/J2EE/MavenWorkSpace/java_safe_demo/src/main/java/pers/zhang/jna/struct_array_param.dylib", LibStructArrayParam.class);
class Person extends Structure {
public int age;
public byte[] name = new byte[20];
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[]{"age", "name"});
}
}
//描述函數(shù)
int changeObjs(Person[] per, int size);
}
public static void main(String[] args) {
LibStructArrayParam.Person per = new LibStructArrayParam.Person();
LibStructArrayParam.Person[] pers = (LibStructArrayParam.Person[]) per.toArray(2);
pers[0].age = 1;
pers[0].name = Arrays.copyOf("tom".getBytes(), 20);
pers[1].age = 2;
pers[1].name = Arrays.copyOf("jerry".getBytes(), 20);
LibStructArrayParam.INSTANCE.changeObjs(pers, 2);
}
}
輸出:
person[0] age:10 person[0] name:wokettas person[1] age:20 person[1] name:wokettas
5、常見問題
5.1、Java 類的字段聲明必須與重寫方法保持一致
類中變量名要和重寫后的方法中的保持一致(名稱不一致,變量個數(shù)不一致都會失敗),否則編譯不通過。


Java 中模擬結構體時,類名可以和 C 的結構體名稱不同,只需要 Java 類的各個字段名稱、 各字段順序?qū)Y構體中的字段即可。
5.2、Java映射C數(shù)組亂碼問題
文件array_param.c
#include <stdio.h>
#include <string.h>
typedef struct {
int enable;
char static_ip[20];
char netmask[20];
char gateway[20];
char dns1[20];
char dns2[20];
}network_eth;
int sdk_set_network_eth(const char *ip, network_eth *network_param)
{
if (strlen(ip) == 0 || network_param == NULL)
{
printf("sdk_set_network_eth param error!\n");
return -1;
}
printf("ip:%s\n",ip);
printf("network_eth enable:%d\n",network_param->enable);
printf("network_eth static_ip:%s\n",network_param->static_ip);
printf("network_eth netmask:%s\n",network_param->netmask);
printf("network_eth gateway:%s\n",network_param->gateway);
printf("network_eth dns1:%s\n",network_param->dns1);
printf("network_eth dns2:%s\n",network_param->dns2);
return 0;
}
編譯為共享庫array_param.dylib:
gcc -I $JAVA_HOME/include -I $JAVA_HOME/include/darwin -dynamiclib -o array_param.dylib array_param.c
JNA調(diào)用:
public class CharArrayTest {
public interface LibCharArray extends Library {
LibCharArray INSTANCE = Native.load("/Users/acton_zhang/J2EE/MavenWorkSpace/java_safe_demo/src/main/java/pers/zhang/jna/array_param.dylib", LibCharArray.class);
class NetWorkEth extends Structure {
public int enable;
public byte[] static_ip = new byte[20];
public byte[] netmask = new byte[20];
public byte[] gateway = new byte[20];
public byte[] dns1 = new byte[20];
public byte[] dns2 = new byte[20];
public static class ByReference extends NetWorkEth implements Structure.ByReference{}
public static class ByValue extends NetWorkEth implements Structure.ByValue{}
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[] {"enable","static_ip","netmask","gateway","dns1","dns2"});
}
}
int sdk_set_network_eth(byte[] ip, NetWorkEth.ByReference netWork);
}
public static void main(String[] args) {
String ip = "127.0.0.1";
LibCharArray.NetWorkEth.ByReference netWorkEth = new LibCharArray.NetWorkEth.ByReference();
netWorkEth.enable = 1;
netWorkEth.static_ip = "10.20.6.10".getBytes();
netWorkEth.netmask = "255.255.255.0".getBytes();
netWorkEth.gateway = "192.168.122.134".getBytes();
netWorkEth.dns1 = "114.114.114.114".getBytes();
netWorkEth.dns2 = "8.8.8.8".getBytes();
int res = LibCharArray.INSTANCE.sdk_set_network_eth(ip.getBytes(), netWorkEth);
System.out.println(res);
}
輸出:
0 ip:127.0.0.1 network_eth enable:1 network_eth static_ip:10.20.6.10255.255.255.0192.168.122.134114.114.114.1148.8.8.8127.0.0.1 network_eth netmask:5.0192.168.122.134114.114.114.1148.8.8.8127.0.0.1 network_eth gateway:4.114.114.1148.8.8.8127.0.0.1 network_eth dns1:127.0.0.1 network_eth dns2:??#mRealVarArgsCheckerlang/O
輸出亂碼!因為C/C++的數(shù)組類型在內(nèi)存中是連續(xù)存儲的,而Java的數(shù)組不一定是連續(xù)的。對C中的char數(shù)組類型賦值時,不能直接給數(shù)組賦值,要使用Arrays.copyOf(String.getBytes(), 20)賦值,數(shù)組長度和C結構體中聲明的長度保持一致。
在某些情況下,雖然使用 String.getBytes()轉換也能成功,但大多數(shù)情況下,使用該方法會出 現(xiàn)亂碼,不建議使用 String.getBytes()。
public static void main(String[] args) {
String ip = "127.0.0.1";
byte[] ipArr = Arrays.copyOf(ip.getBytes(), 20);
LibCharArray.NetWorkEth.ByReference netWorkEth = new LibCharArray.NetWorkEth.ByReference();
netWorkEth.enable = 1;
netWorkEth.static_ip = Arrays.copyOf("10.20.6.10".getBytes(), 20);
netWorkEth.netmask = Arrays.copyOf("255.255.255.0".getBytes(), 20);
netWorkEth.gateway = Arrays.copyOf("192.168.122.134".getBytes(), 20);
netWorkEth.dns1 = Arrays.copyOf("114.114.114.114".getBytes(), 20);
netWorkEth.dns2 = Arrays.copyOf("8.8.8.8".getBytes(), 20);
int res = LibCharArray.INSTANCE.sdk_set_network_eth(ipArr, netWorkEth);
System.out.println(res);
}
輸出:
0 ip:127.0.0.1 network_eth enable:1 network_eth static_ip:10.20.6.10 network_eth netmask:255.255.255.0 network_eth gateway:192.168.122.134 network_eth dns1:114.114.114.114 network_eth dns2:8.8.8.8
創(chuàng)建結構體數(shù)組應該使用 JNA 的 toArray()方法,而不是 Java 常規(guī)創(chuàng)建數(shù)組的方法,因為內(nèi) 存空間在 java 中是不連續(xù)的,jna 定義數(shù)組是需要使用 toArray 方法,這樣實例化出來的數(shù) 組內(nèi)存空間是連續(xù)的。
5.3、Java 接收 C 函數(shù)返回類型為 char*
JNA 使用 String 無法直接接收 C 函數(shù)返回類型為 char*的值,必須要用 Pointer 進行接收。
當 C 函數(shù)的參數(shù)為 char*、const char*用做輸入時,JNA 可以使用 String 類型進行傳 參,此時可以正常調(diào)用 C 函數(shù);但當C函數(shù)的參數(shù)類型為char*且用作輸出時,使用 String 類型無法正常接收,必須使用 Pointer 進行處理。
文件char_out.c
#include <stdio.h>
#include <string.h>
void append_str(char* str, char* append)
{
printf("str:%s\n", str);
printf("append:%s\n", append);
int length = strlen(str);
char* p = str;
for(int i = 0; i < length; i++)
{
p++;
}
strcpy(p, append);
}
編譯為共享庫char_out.dylib:
gcc -I $JAVA_HOME/include -I $JAVA_HOME/include/darwin -dynamiclib -o char_out.dylib char_out.c
JNA調(diào)用,使用Pointer接收char*返回值:
public class CharOutTest {
public interface LibCharOut extends Library {
LibCharOut INSTANCE = Native.load("/Users/acton_zhang/J2EE/MavenWorkSpace/java_safe_demo/src/main/java/pers/zhang/jna/char_out.dylib", LibCharOut.class);
void append_str(Pointer str, String append);
}
public static void main(String[] args) {
Pointer str = new Memory(40);
str.setString(0, "hello");
String append = " world";
LibCharOut.INSTANCE.append_str(str, append);
System.out.println(str.getString(0));
}
}輸出:
hello world str:hello append: world
通過 Java 獲取 char * 字符串,必須要通過 Java 傳入一個 com.sun.jna.Pointer 指針變量,然后在 DLL 中將值賦給此指針變量,然后通過此指針變量獲取值。
5.4、動態(tài)庫和 jdk 的位數(shù)必須匹配
64 位的 jdk 只能調(diào)用 64 位的 dll,32 位也一樣。如果使用的 jdk 和 dll 位數(shù)不同,會報 Unable to load DLL 'xxx.dll': 找不到指定的模塊或者java.lang.UnsatisfiedLinkError: %1 不是有效的 Win32 應用程序。
5.5、動態(tài)庫版本必須和系統(tǒng)類型匹配
- windows:后綴名為
.dll - linux:后綴名為
.so - mac:后綴名為
.dylib
總結
到此這篇關于Java本地方法(JNA)詳解及常見問題的文章就介紹到這了,更多相關Java本地方法JNA內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
修改SpringBoot 中MyBatis的mapper.xml文件位置的過程詳解
由于MyBatis默認的mapper.xml的掃描位置是resource文件下,但是不可能整個項目的mapper.xml文件都放在resource下,如果文件較少還行,但是如果文件比較多,太麻煩了,所以本文給大家介紹了修改SpringBoot 中MyBatis的mapper.xml文件位置的過程,需要的朋友可以參考下2024-08-08
java.exe和javaw.exe的區(qū)別及使用方法
這篇文章主要介紹了java.exe和javaw.exe的區(qū)別及使用方法,需要的朋友可以參考下2014-04-04
微服務Redis-Session共享登錄狀態(tài)的過程詳解
這篇文章主要介紹了微服務Redis-Session共享登錄狀態(tài),本文采取Spring security做登錄校驗,用redis做session共享,實現(xiàn)單服務登錄可靠性,微服務之間調(diào)用的可靠性與通用性,需要的朋友可以參考下2023-12-12
SpringBoot實現(xiàn)發(fā)送QQ郵件的示例代碼
這篇文章主要介紹了SpringBoot如何實現(xiàn)發(fā)送QQ郵件功能,本文通過實例圖文相結合給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-09-09
SpringBoot中使用異步調(diào)度程序的高級方法
本文主要介紹了SpringBoot中使用異步調(diào)度程序的高級方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2024-07-07

