欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java本地方法(JNA)詳解及常見問題

 更新時間:2024年09月12日 10:06:57   作者:吳聲子夜歌  
JNA(Java?Native?Access)是一個開源Java框架,用于無需編寫JNI代碼即可動態(tài)訪問本地系統(tǒng)庫如Windows的dll,它允許Java程序直接調(diào)用本地方法,這篇文章主要介紹了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  

PackageDescription
com.sun.jnaProvides simplified native library access.

提供簡化的本機(jī)庫訪問權(quán)限。

com.sun.jna.ptrProvides various native pointer-to-type ( <type> *) representations.

提供各種母語指針到類型(<類型> *)表示。

com.sun.jna.win32Provides type and function mappers required for standard APIs on the Windows platform.

提供Windows平臺上標(biāo)準(zhǔn)API所需的類型和功能映射器。

Platform Utilities  

PackageDescription
com.sun.jna.platformProvides cross-platform utilities based on platform-specific libraries.

根據(jù)特定于平臺的庫提供跨平臺實用程序。

com.sun.jna.platform.dndProvides integrated, extended drag and drop functionality, allowing ghosted drag images to be used on all platforms.

提供集成的擴(kuò)展拖放和丟棄功能,允許縮重拖動圖像在所有平臺上使用。

Platform Specific  

PackageDescription
com.sun.jna.platform.linuxProvides common library mappings for Linux.

為Linux提供公共圖書館映射。

com.sun.jna.platform.macProvides common library mappings for the OS X platform.

為OS X平臺提供公共庫映射。

com.sun.jna.platform.unixProvides common library mappings for Unix and X11-based platforms.

為基于UNIX和X11的平臺提供公共圖書館映射。

com.sun.jna.platform.unix.aixProvides common library mappings for the AIX platform.

為AIX平臺提供公共庫映射。

com.sun.jna.platform.unix.solarisProvides common library mappings for the Solaris (SunOS) platform.

為Solaris(Sunos)平臺提供公共圖書館映射。

com.sun.jna.platform.win32Provides common library mappings for the Windows platform.

為Windows平臺提供公共庫映射。

com.sun.jna.platform.win32.COMProvides common library mappings for Windows Component Object Model (COM).

為Windows組件對象模型(COM)提供公共庫映射。

com.sun.jna.platform.win32.COM.tlbProvides common library mappings for COM Type Libraries.

為COM類型庫提供公共庫映射。

com.sun.jna.platform.win32.COM.tlb.impProvides common library mappings for COM Type Library implementations.

為COM類型庫實現(xiàn)提供公共庫映射。

com.sun.jna.platform.win32.COM.utilProvides COM Utilities

提供Com Utilities.

com.sun.jna.platform.win32.COM.util.annotationProvides COM Utility annotations

提供com實用程序注釋

com.sun.jna.platform.winceProvides common library mappings for the Windows CE platform.

為Windows CE平臺提供公共庫映射。

Other Packages  

PackageDescription
com.sun.jna.internalProvides 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.ByReferenceStructure.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)文章

  • Java根據(jù)控制臺實現(xiàn)定位異常

    Java根據(jù)控制臺實現(xiàn)定位異常

    這篇文章主要介紹了Java根據(jù)控制臺定位異常,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-05-05
  • 修改SpringBoot 中MyBatis的mapper.xml文件位置的過程詳解

    修改SpringBoot 中MyBatis的mapper.xml文件位置的過程詳解

    由于MyBatis默認(rèn)的mapper.xml的掃描位置是resource文件下,但是不可能整個項目的mapper.xml文件都放在resource下,如果文件較少還行,但是如果文件比較多,太麻煩了,所以本文給大家介紹了修改SpringBoot 中MyBatis的mapper.xml文件位置的過程,需要的朋友可以參考下
    2024-08-08
  • java.exe和javaw.exe的區(qū)別及使用方法

    java.exe和javaw.exe的區(qū)別及使用方法

    這篇文章主要介紹了java.exe和javaw.exe的區(qū)別及使用方法,需要的朋友可以參考下
    2014-04-04
  • 關(guān)于IDEA配置文件字符集的問題

    關(guān)于IDEA配置文件字符集的問題

    這篇文章主要介紹了關(guān)于IDEA配置文件字符集的問題,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-12-12
  • Mac?M1安裝JDK的實戰(zhàn)避坑指南

    Mac?M1安裝JDK的實戰(zhàn)避坑指南

    這篇文章主要給大家介紹了關(guān)于Mac?M1安裝JDK避坑的相關(guān)資料,文中通過圖文介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2023-02-02
  • 微服務(wù)Redis-Session共享登錄狀態(tài)的過程詳解

    微服務(wù)Redis-Session共享登錄狀態(tài)的過程詳解

    這篇文章主要介紹了微服務(wù)Redis-Session共享登錄狀態(tài),本文采取Spring security做登錄校驗,用redis做session共享,實現(xiàn)單服務(wù)登錄可靠性,微服務(wù)之間調(diào)用的可靠性與通用性,需要的朋友可以參考下
    2023-12-12
  • 一文帶你深入剖析Java線程池的前世今生

    一文帶你深入剖析Java線程池的前世今生

    這篇文章主要帶大家介紹了深入剖析一下Java線程池的前世今生,了解線程池的原理以及為什么需要線程池。文中的示例代碼講解詳細(xì),需要的可以參考一下
    2022-10-10
  • SpringBoot實現(xiàn)發(fā)送QQ郵件的示例代碼

    SpringBoot實現(xiàn)發(fā)送QQ郵件的示例代碼

    這篇文章主要介紹了SpringBoot如何實現(xiàn)發(fā)送QQ郵件功能,本文通過實例圖文相結(jié)合給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-09-09
  • spring boot 配置動態(tài)刷新詳解

    spring boot 配置動態(tài)刷新詳解

    這篇文章主要介紹了spring boot 配置動態(tài)刷新實現(xiàn)詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2021-09-09
  • SpringBoot中使用異步調(diào)度程序的高級方法

    SpringBoot中使用異步調(diào)度程序的高級方法

    本文主要介紹了SpringBoot中使用異步調(diào)度程序的高級方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-07-07

最新評論