当前位置:网站首页>【混合编程jni 】第十一篇之JNA详情

【混合编程jni 】第十一篇之JNA详情

2022-06-26 22:01:00 华为云

继续JNA的了解,如果你还不知道JNA是什么,可以看下我上篇文章

函数的映射

接口映射

通过 Native.load() 加载动态库的时候,直接通过接口对应动态库的函数,

因为JNA 会创建一个代理,代理 invoke通过Library.Handler. 在导出的方法表中找倒匹配的函数。

直接映射

JNA 提供了直接注册的方式,如果有原生的,被标注为 native的函数调用。

可以直接使用Native.register() 注册你的方法

数据类型映射

基础类型直接映射,因为大小相同

image.png

使用指针和数组

原始数组参数(包括结构)由它们对应的 Java 类型表示。例如:

//原始 C 声明 void fill_buffer ( int *buf, int len); void fill_buffer ( int buf[], int len); //与数组语法相同 //等效的 JNA 映射 void fill_buffer( int [] buf, int len);

注意:如果参数要由函数调用范围之外的本机函数使用,则必须使用 Memory 或 NIO 直接缓冲区。Java 原始数组提供的内存仅在函数调用期间由本机代码使用有效。

C 字符串数组(例如char* argv[]Cmain的 )可以用String[]Java 代码表示。JNA 将自动传递具有NULL最终元素的等效数组。

使用结构和联合

当函数需要指向结构的指针时,Java应该使用Structure。如果结构是按值传递或返回的,则只需对参数或返回类型类声明稍作修改即可。

定义类继承自Structure,。并且必须在FieldOrder注解或方法返回的列表中按顺序包含每个声明的字段名称getFieldOrder()。

如果函数需要结构数组(在内存中连续分配), Java 可以使用Structure[。

传入 的数组是Structure时,无需初始化数组元素(函数调用将为您分配、归零内存并分配元素)。

如果确实需要初始化数组,则应使用该Structure.toArray方法获取内存中连续的结构元素数组,然后可以根据需要对其进行初始化。

Unions 通常可以与Structures 互换,但要求您在setType方法正确传递给函数调用之前指示哪个联合字段是活动的。

如果您有特别长或复杂的结构,您可以考虑使用由 Olivier Chafik 编写的JNAerator工具,它可以为您生成 JNA 映射。

使用 ByReference 参数

当一个函数接受指向类型参数的指针时,您可以使用其中一种ByReference类型来捕获返回值,或子类化您自己的值。例如:

//原始 C 声明void  allocate_buffer ( char **bufp, int * lenp);//等效的 JNA 映射void allocate_buffer(PointerByReference bufp, IntByReference lenp);
// UsagePointerByReference pref = new PointerByReference();IntByReference iref = new IntByReference();lib.allocate_buffer(pref, iref);Pointer p = pref.getValue();byte[] buffer = p.getByteArray(0, iref.getValue());

可以使用具有所需类型的单个元素的 Java 数组,但ByReference约定更好地传达了代码的意图。Pointer除了getByteArray()有效地作为类型转换到内存上之外,该类还提供了许多访问器方法。

PointerType可以通过从类派生来声明类型安全指针。

整点例子

1、数组 数组的例子,直接用上面的例子

c++头文件

#ifndef TESTJNA_TEST_H#define TESTJNA_TEST_H#define PDOOL_API extern "C" __declspec( dllexport )PDOOL_API int sum(int a, int b);PDOOL_API void fill_buffer(int buf[], int len);#endif //TESTJNA_TEST_H

c++ 实现

这里只是打印出所有的数组元素

#include "test.h"#include "iostream"int sum(int a ,int b){    return a +b;} void fill_buffer(int buf[], int len){    for (int i = 0;i<len;i++){        std::cout<<buf[i];    }}

将上面代码生成的dll 拷贝到java项目的resource目录,

如果不会的话可以看下我之前的文章

看下java侧的实现

import com.sun.jna.Library;import com.sun.jna.Native;/** * 动态库接口 * @author xin.chong */public interface Lib extends Library {    Lib INSTANCE = Native.load( "testJNA.dll", Lib.class);    int sum(int a,int b);     void fill_buffer(int[] buf, int len);}

看下调用

/** * Hello world! * */public class App {    public static void main(String[] args) {        int result = Lib.INSTANCE.sum(1,3);        System.out.println("恭喜,JNA 第一个程序成功 ,the  result is  "+ result);        int[] arr = {1,2,3,4,5};        Lib.INSTANCE.fill_buffer(arr,arr.length);    }}

看下打印结果

image.png

数组调用还是简单的,当然这只是基础类型的数组,复杂的后面会讲,继续!

2、结构体

结构体属于复杂的自定义结构,所以处理起来还是比较麻烦,下面一起看下在Java中如何模拟结构体的

c++ 侧 定义一个玩家结构体

#ifndef TESTJNA_TEST_H#define TESTJNA_TEST_H#define PDOOL_API extern "C" __declspec( dllexport ) struct PlayerStruct{    long id;    wchar_t* name;    int age;}; PDOOL_API int sum(int a, int b); PDOOL_API void fill_buffer(int buf[], int len); PDOOL_API void createPlayer(PlayerStruct* pUserStruct); #endif //TESTJNA_TEST_Hcpp 实现void createPlayer(PlayerStruct* pPlayerStruct){    std::cout<<pPlayerStruct->id <<" "<< pPlayerStruct->name <<"  "<<pPlayerStruct->age;}

Java侧的实现

先创建一个类继承自 Structure 和 C++ 侧进行对应映射

import com.sun.jna.Structure; import java.util.Arrays;import java.util.List; public class PlayerStruct extends Structure {    public long id;    public String name;    public int age;    public static class ByReference extends PlayerStruct implements Structure.ByReference {}    public static class ByValue extends PlayerStruct implements Structure.ByValue {}    @Override    protected List<String> getFieldOrder() {        return Arrays.asList(new String[] { "id", "name", "age"});    }}

注意点:

必须要继承 Structure getFieldOrder 返回的字段列表要和C++侧保持一致,顺序不能乱,对应了c++ 侧的内存顺序 ByReference 对应c++侧的结构体指针 ByValue 是结构体的值



原网站

版权声明
本文为[华为云]所创,转载请带上原文链接,感谢
https://bbs.huaweicloud.com/blogs/361906