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

“持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第14天,点击查看活动详情

继续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);
复制代码
// Usage
PointerByReference 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_H
cpp 实现

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 是结构体的值

猜你喜欢

转载自juejin.im/post/7108377747289800741