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

举报
香菜聊游戏 发表于 2022/06/26 21:34:07 2022/06/26
【摘要】 继续JNA的了解,如果你还不知道JNA是什么,可以看下我上篇文章函数的映射接口映射通过 Native.load() 加载动态库的时候,直接通过接口对应动态库的函数,因为JNA 会创建一个代理,代理 invoke通过Library.Handler. 在导出的方法表中找倒匹配的函数。直接映射JNA 提供了直接注册的方式,如果有原生的,被标注为 native的函数调用。可以直接使用Native.r...

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



【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。