【混合编程jni 】第十一篇之JNA详情
继续JNA的了解,如果你还不知道JNA是什么,可以看下我上篇文章
函数的映射
接口映射
通过 Native.load() 加载动态库的时候,直接通过接口对应动态库的函数,
因为JNA 会创建一个代理,代理 invoke通过Library.Handler. 在导出的方法表中找倒匹配的函数。
直接映射
JNA 提供了直接注册的方式,如果有原生的,被标注为 native的函数调用。
可以直接使用Native.register() 注册你的方法
数据类型映射
基础类型直接映射,因为大小相同
使用指针和数组
原始数组参数(包括结构)由它们对应的 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);
}
}
看下打印结果
数组调用还是简单的,当然这只是基础类型的数组,复杂的后面会讲,继续!
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 是结构体的值
- 点赞
- 收藏
- 关注作者
评论(0)