Java本地方法(JNA)詳解及常見問題
JNA
1、概述
JNA 全稱 Java Native Access,是一個建立在經(jīng)典的 JNI 技術(shù)之上的 Java 開源框架。JNA 提供一組 Java 工具類用于在運行期動態(tài)訪問系統(tǒng)本地庫(native library:如 Window 的 dll)而不需要編寫任何 Native/JNI 代碼。開發(fā)人員只要在一個 java 接口中描述目標(biāo) native library 的函數(shù)與結(jié)構(gòu),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. 提供簡化的本機(jī)庫訪問權(quán)限。 |
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平臺上標(biāo)準(zhǔn)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. 提供集成的擴(kuò)展拖放和丟棄功能,允許縮重拖動圖像在所有平臺上使用。 |
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 { /** * 定義一個接口,默認(rèn)的是繼承Library,如果動態(tài)鏈接庫里額函數(shù)是以stdcall方式輸出的,那么就繼承StdCallLibrary * 這個接口對應(yīng)一個動態(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)建接口的實例 * 共享庫的查找順序是: * 先從當(dāng)前類的當(dāng)前文件夾找,如果沒找到 * 再從工程當(dāng)前文件夾下面找,如果找不到 * 最后在當(dāng)前平臺下面去搜索 */ CLibrary INSTANCE = Native.load("c", CLibrary.class); /** * 接口中只需要定義要用到的函數(shù)或者公共變量,不需要的可以不定義 * z注意參數(shù)和返回值的類型,應(yīng)該和共享庫中的函數(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
,運行結(jié)果如下:
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框架,目標(biāo)函數(shù)是C/C++是有地址變量的,很多時候都需要將變量的結(jié)果帶回,因此,地址傳遞在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("求和結(jié)果:" + add); System.out.println("c:" + c); System.out.println("msg:" + msg); } }
結(jié)果顯而易見,無論add函數(shù)對c和msg做了何種改變,返回java中,值都不會變更。甚至?xí)驗槲覀儗和msg賦值導(dǎo)致C函數(shù)訪問到奇怪的地址,導(dǎo)致報錯。
2.2、Pointer類
JNA框架提供了com.sun.jna.Pointer
,指針數(shù)據(jù)類型,用于匹配轉(zhuǎn)換映射函數(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設(shè)置/獲取值:
Pointer的setXxx方法提供了為各種類型設(shè)置值的方法:
第一個參數(shù)為偏移量,第二個參數(shù)為值。
//設(shè)置int int_pointer.setInt(0, 123); //設(shè)置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時重復(fù)執(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("求和結(jié)果:" + 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時重復(fù)執(zhí)行Native.free()方法 Native.free(Pointer.nativeValue(msg)); Pointer.nativeValue(msg, 0); } }
輸出:
求和結(jié)果:25 c:50 msg:hello world!
3、引用對象ByReference
JNA框架提供了com.sun.jna.ptr.ByReference
,引用對象類型,提供通用的“指向類型的指針”功能,通常在C代碼中用于向調(diào)用方返回值以及函數(shù)結(jié)果。
3.1、使用場景
在低版本的JNA中,如果C中函數(shù)執(zhí)行失敗時,沒有對指針進(jìn)行處理,那么使用Pointer就會得到一個垃圾值。
C函數(shù)文件test.c:
int test_pointer(int a, int *b) { if (a < 0) { //未對*b進(jìn)行處理 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ù)類型都對應(yīng)子類供使用比如int的用IntByReference,字符串的使用PointerByReference。
創(chuàng)建ByReference:
//無參構(gòu)造,默認(rèn)值為0 IntByReference intRef = new IntByReference(); //有參構(gòu)造,根據(jù)指定值創(chuàng)建 IntByReference intRef = new IntByReference(4);
設(shè)置/獲取值:
//設(shè)置 intRef.setValue(10); //獲取 intRef.getValue();
3.3、案例
int test_pointer(int a, int *b) { if (a < 0) { //未對*b進(jìn)行處理 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)存通過垃圾回收機(jī)制自動完成
總的來講ByReference更加方便,但是對于多層的指針引用,可能Pointer更合適處理嵌套結(jié)構(gòu)。
4、Java模擬C結(jié)構(gòu)體
4.1、使用場景
結(jié)構(gòu)體作為參數(shù)有兩個點,一個是傳入,一個是返回。傳入的還分傳值和傳引用。
目標(biāo)C程序,struct.c:
#include <stdio.h> #include <stdlib.h> #include <string.h> // teacher 結(jié)構(gòu)體定義 typedef struct { int tea_age; char *tea_name; } Teacher; // student 結(jié)構(gòu)體定義 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復(fù)制給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)容復(fù)制給結(jié)果變量teacher,用于函數(shù)返回【驗證是否能返回結(jié)構(gòu)體】
- 改變傳入結(jié)構(gòu)體變量tea的值【驗證結(jié)構(gòu)體引用傳遞是否生效】
編譯為共享庫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); //定義結(jié)構(gòu)體 class TeacherStruct extends Structure { //結(jié)構(gòu)體參數(shù)類型及順序要嚴(yán)格按照C結(jié)構(gòu)體的類型及順序 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(); } } //定義結(jié)構(gòu)體 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 = "小學(xué)生"; teaByReference.tea_age = 48; teaByReference.tea_name = "高級教師"; // 調(diào)用函數(shù)之前 System.out.println("調(diào)用函數(shù)之前teaByReference:" + teaByReference.toString()); // 調(diào)用方法。返回結(jié)果 LibStruct.TeacherStruct.ByValue result = LibStruct.INSTANCE.stuTea(stuByValue, teaByReference); // 查看傳地址的teaByReference的值是否變更 System.out.println("調(diào)用函數(shù)之后teaByReference:" + teaByReference.toString()); // 函數(shù)返回結(jié)果 System.out.println("調(diào)用函數(shù)返回結(jié)果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ù)返回結(jié)果result.name:TeacherStruct{tea_age=18, tea_name='小學(xué)生'} StructTest$LibStruct$TeacherStruct$ByValue(auto-allocated@0x7ffc3620b480 (16 bytes)) { int tea_age@0x0=0x0012 String tea_name@0x8=小學(xué)生 } stu-name=小學(xué)生/nstu-age=18/n
4.2、Structure類
要使用 Java 類模擬 C 的結(jié)構(gòu)體,需要 Java 類繼承Structure類。
必須要注意,Structure子類中的公共字段的順序,必須與 C 語言中的結(jié)構(gòu)的順序保持一致,否則會報錯!
因為,Java 調(diào)用動態(tài)鏈接庫中的 C 函數(shù),實際上就是一段內(nèi)存作為函數(shù)的參數(shù)傳遞給 C 函數(shù)。動態(tài)鏈接庫以為這個參數(shù)就是 C 語言傳過來的參數(shù)。同時,C 語言的結(jié)構(gòu)體是一個嚴(yán)格的規(guī)范,它定義了內(nèi)存的次序。因此,JNA 中模擬的結(jié)構(gòu)體的變量順序絕對不能錯。
如果一個 Struct 有 2 個 int 變量 int a, int b,如果 JNA 中的次序和 C 語言中的次序相反,那么不會報錯,但是數(shù)據(jù)將會被傳遞到錯誤的字段中去。
另外,Structure類有兩個內(nèi)部接口Structure.ByReference
和Structure.ByValue
。
這兩個接口僅僅是標(biāo)記:
- 如果一個類實現(xiàn) Structure.ByReference 接口,就表示這個類代表結(jié)構(gòu)體指針。
- 如果一個類實現(xiàn) Structure.ByValue 接口,就表示這個類代表結(jié)構(gòu)體本身。
- 如果不實現(xiàn)這兩個接口,那么就相當(dāng)于你實現(xiàn)了 Structure.ByReference 接口。
使用這兩個接口的實現(xiàn)類,可以明確定義我們的 Structure 實例表示的是結(jié)構(gòu)體指針還是結(jié)構(gòu)體本身。
4.3、結(jié)構(gòu)體本身作為參數(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); //定義結(jié)構(gòu)體 class UserStruct extends Structure { //公共字段的順序,必須與C語言中的結(jié)構(gòu)的順序保持一致 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、結(jié)構(gòu)體指針作為參數(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); //定義結(jié)構(gòu)體 class UserStruct extends Structure { //公共字段的順序,必須與C語言中的結(jié)構(gòu)的順序保持一致 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、嵌套結(jié)構(gòu)體本身作為參數(shù)
C 語言最復(fù)雜的數(shù)據(jù)類型就是結(jié)構(gòu)體。結(jié)構(gòu)體的內(nèi)部可以嵌套結(jié)構(gòu)體,這使它可以模擬任何類型的對象。JNA 也可以模擬這類復(fù)雜的結(jié)構(gòu)體,結(jié)構(gòu)體內(nèi)部可以包含結(jié)構(gòu)體對象指針的數(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結(jié)構(gòu)體 class UserStruct extends Structure { //公共字段的順序,必須與C語言中的結(jié)構(gòu)的順序保持一致 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結(jié)構(gòu)體 class CompanyStruct extends Structure { //公共字段的順序,必須與C語言中的結(jié)構(gòu)的順序保持一致 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有限責(zé)任公司"; 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有限責(zé)任公司 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、嵌套結(jié)構(gòu)體指針作為參數(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結(jié)構(gòu)體 class UserStruct extends Structure { //公共字段的順序,必須與C語言中的結(jié)構(gòu)的順序保持一致 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結(jié)構(gòu)體 class CompanyStruct extends Structure { //公共字段的順序,必須與C語言中的結(jié)構(gòu)的順序保持一致 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有限責(zé)任公司"; 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有限責(zé)任公司 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、結(jié)構(gòu)體中嵌套結(jié)構(gòu)體數(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ù)
結(jié)構(gòu)體中嵌套結(jié)構(gòu)體數(shù)組用作輸出參數(shù)時,需要對結(jié)構(gòu)體數(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、結(jié)構(gòu)體數(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ù)類型改為結(jié)構(gòu)體本身即可,即不帶 ByReference 或 ByValue。結(jié)構(gòu)體數(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)
結(jié)構(gòu)體數(shù)組必須使用連續(xù)的內(nèi)存區(qū)域。p1,p2 都是 new 出來的對象,不可能連續(xù),用傳統(tǒng)方式初始化數(shù)組不能解決。
應(yīng)使用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 中模擬結(jié)構(gòu)體時,類名可以和 C 的結(jié)構(gòu)體名稱不同,只需要 Java 類的各個字段名稱、 各字段順序?qū)?yīng)結(jié)構(gòu)體中的字段即可。
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結(jié)構(gòu)體中聲明的長度保持一致。
在某些情況下,雖然使用 String.getBytes()轉(zhuǎn)換也能成功,但大多數(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)建結(jié)構(gòu)體數(shù)組應(yīng)該使用 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 進(jìn)行接收。
當(dāng) C 函數(shù)的參數(shù)為 char*、const char*用做輸入時,JNA 可以使用 String 類型進(jìn)行傳 參,此時可以正常調(diào)用 C 函數(shù);但當(dāng)C函數(shù)的參數(shù)類型為char*且用作輸出時,使用 String 類型無法正常接收,必須使用 Pointer 進(jìn)行處理。
文件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 應(yīng)用程序。
5.5、動態(tài)庫版本必須和系統(tǒng)類型匹配
- windows:后綴名為
.dll
- linux:后綴名為
.so
- mac:后綴名為
.dylib
總結(jié)
到此這篇關(guān)于Java本地方法(JNA)詳解及常見問題的文章就介紹到這了,更多相關(guān)Java本地方法JNA內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
修改SpringBoot 中MyBatis的mapper.xml文件位置的過程詳解
由于MyBatis默認(rèn)的mapper.xml的掃描位置是resource文件下,但是不可能整個項目的mapper.xml文件都放在resource下,如果文件較少還行,但是如果文件比較多,太麻煩了,所以本文給大家介紹了修改SpringBoot 中MyBatis的mapper.xml文件位置的過程,需要的朋友可以參考下2024-08-08java.exe和javaw.exe的區(qū)別及使用方法
這篇文章主要介紹了java.exe和javaw.exe的區(qū)別及使用方法,需要的朋友可以參考下2014-04-04微服務(wù)Redis-Session共享登錄狀態(tài)的過程詳解
這篇文章主要介紹了微服務(wù)Redis-Session共享登錄狀態(tài),本文采取Spring security做登錄校驗,用redis做session共享,實現(xiàn)單服務(wù)登錄可靠性,微服務(wù)之間調(diào)用的可靠性與通用性,需要的朋友可以參考下2023-12-12SpringBoot實現(xiàn)發(fā)送QQ郵件的示例代碼
這篇文章主要介紹了SpringBoot如何實現(xiàn)發(fā)送QQ郵件功能,本文通過實例圖文相結(jié)合給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-09-09SpringBoot中使用異步調(diào)度程序的高級方法
本文主要介紹了SpringBoot中使用異步調(diào)度程序的高級方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-07-07