当前位置:网站首页>JNA学习笔记一:概念

JNA学习笔记一:概念

2022-07-07 11:00:00 山鬼谣me

默认情况下,所有Structure对象在本机函数调用之前,都将其Java字段复制到其本机内存中,并在调用后复制回来。

默认类型映射

Java 原始类型(及其对象等价物)直接映射到相同大小的本机 C 类型。

NativeTypeSizeJava TypeCommon Windows Types
char8-bit integerbyteBYTE, TCHAR
short16-bit integershortWORD
wchar_t16/32-bit charactercharTCHAR
int32-bit integerintDWORD
intboolean valuebooleanBOOL
long32/64-bit integerNativeLongLONG
longlong 64-bit integerlong__int64
float32-bit FPfloat
double64-bit FPdouble
char*C stringStringLPCSTR
void*pointerPointerLPVOID, HANDLE, LPXXX

无符号类型使用与有符号类型相同的映射。 C 枚举通常可以与“int”互换。

使用指针和数组

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

// 原始 C 声明
void fill_buffer(int *buf, int len);
//Java的写法
void fill_buffer(int buf[], int len); // same thing with array syntax
// 等效 JNA 映射
void fill_buffer(int[] buf, int len);

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

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

使用Structures 和 Unions

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

通常,我们需要自己定义Structure的派生的公共静态类。即:static class AttachOptions extends Structure类似这样。
这允许结构共享为库接口定义的任何选项(如自定义类型映射)。您必须在 FieldOrder 注释或 getFieldOrder() 方法返回的列表中按顺序包含每个声明的字段名称。

如果函数需要结构数组(在内存中连续分配),则可以使用 Java Structure[]。传入一个 Structure 数组时,不需要初始化数组元素(函数调用会为你分配、归零内存,并为你分配元素)。如果确实需要初始化数组,则应使用 Structure.toArray 方法获取内存中连续的 Structure 元素数组,然后可以根据需要对其进行初始化。

Unions通常可以与结构互换,但要求您使用 setType 方法指示哪个Unions字段处于活动状态,然后才能将其正确传递给函数调用。

https://github.com/java-native-access/jna/blob/master/www/StructuresAndUnions.md

使用 ByReference 参数

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

// 原始 C 声明
void allocate_buffer(char **bufp, int* lenp);

// 等效 JNA 映射
void allocate_buffer(PointerByReference bufp, IntByReference lenp);

// 用法
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 更好地传达了代码的意图。除了 getByteArray() 之外,Pointer 类还提供了许多访问器方法,它们有效地用作内存上的类型转换。

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

https://github.com/java-native-access/jna/blob/master/www/ByRefArguments.md#using-byreference-arguments

在参考:JNI便捷开发框架JNA框架之结构参数体传递(四) 这篇文章后,实践得出ByReference传递可以修改结构体中的成员变量值,ByValue传递不可以。

从 Java 到 Native 的自定义映射

TypeMapper 类和相关接口提供将用作参数、返回值或结构成员的任何 Java 类型转换为或从本机类型转换。示例 Win32 API 接口使用类型映射器将 Java 布尔值转换为 Win32 BOOL 类型。 TypeMapper 实例作为传递给 Native.load 的选项映射中的 TYPE_MAPPER 键的值传递。

或者,用户定义的类型可以实现 NativeMapped 接口,该接口确定在逐个类的基础上与本机类型之间的转换。

您还可以自定义 Java 方法名称到相应的本机函数名称的映射。 StdCallFunctionMapper 是一种从 Java 接口方法签名自动生成 std​​call 修饰的函数名称的实现。映射器应作为传递给 Native.load 调用的选项映射中的 OPTION_FUNCTION_MAPPER 键的值传递。

https://github.com/java-native-access/jna/blob/master/www/CustomMappings.md

涉及C语言的知识

一、普通变量和的本质区别

在说到普通变量和指针变量的区别时,我更喜欢从一个更高的视角去看待这两者。首先来说,这两者都是变量,既然是变量,就会包含地址和值这两部分,例如int a , 用&a获得该变量的地址,用a获得该变量的值;普通变量和指针变量的区别就是,这两种变量的值的所表示的意义不同, 一般来说,普通变量的值,只是供程序员所使用的值,而指针变量的值则不同,它的值存放的是其他变量的地址。既然普通变量和指针变量是有所区别的,那么声明一个指针变量就必须与普通变量有所区别,c语言用int* b 声明变量b是一个指针变量,即变量b的值是可以解析成另一个变量的地址的。

int a=3;
int* b=&a;

变量a的值a是3,地址&a假设是00E1FEA0;

变量b的值b就是变量a的地址00E1FEA0,&b表示b的地址,一般来说不怎么关心,b表示获得地址为b(要清楚,这个是变量a的地址)的变量所对应的值,也就是说b=3;

将C语言文件打包成共享库文件命令

# 注意最后两个参数 libhello.so(自己的指定名称) hello.c(c文件)
gcc -fPIC -I $JAVA_HOME/include -I $JAVA_HOME/include/linux -shared -o libhello.so hello.c

总结

在参考:JNI便捷开发框架JNA框架之入门(一) 系列文章后,了解到了JNA是怎么工作的。

  1. Java接口签名和C文件(xxx.c文件)中方法签名要一直。类型之间的转换,需要注意。一般类型和Java可以简单对应,字符串类型C语言中是char* str,但是int *c这样的情况,Java中就需要使用Pointer来对应。
  2. Pointer虽然可以满足C语言中的指针类型,但是其内存管理也和C语言一样,需要我们手动维护,所以就有了ByReference类及派生类IntByReference(int)、PointerByReference(字符串)等由Java自动管理内存的类。Pointer类Reference类大体上后者更优,但是对于更多层的指针引用,可能Pointer更合适。
原网站

版权声明
本文为[山鬼谣me]所创,转载请带上原文链接,感谢
https://blog.csdn.net/u013066244/article/details/125516710