java使用jna调用c函数

it2025-07-18  6

java使用jna调用c函数

jna和jni数据类型对应关系编码示例c函数:jna

jna和jni

JNI允许Java代码和其他语言(尤其C/C++)写的代码进行交互,只要遵守调用约定即可。不过使用JNI调用.dll/.so共享库据说非常痛苦。如果已有一个编译好的.dll/.so文件,如果使用JNI技术调用,我们首先需要使用C语言另外写一个.dll/.so共享库,使用SUN规定的数据结构替代C语言的数据结构,调用已有的 dll/so中公布的函 数。然后再在Java中载入这个库dll/so,最后编写Java native函数作为链接库中函数的代理。经过这些繁琐的步骤才能在Java中调用 本地代码。因此,很少有Java程序员愿意编写调用dll/.so库中原生函数的java程序。

JNA框架解决了既需要编写java代码,又要编写C语言的代理方法及很多数据类型的转换的问题,它提供一组Java工具类用于在运行期动态访问系统本地共享类库而不需要编写任何Native/JNI代码。开发人员只要在一个java接口中描述目标native library的函数与结构,JNA将自动实现Java接口到native function的映射,大大降低了Java调用本体共享库的开发难度。

就开发难度来说,JNA要比JNI简单很多。不过JNA相当于封装后的JNI,可以更方便的调用c/c++函数,但是使用JNA只能单方面调用c/c++函数,使用JNI则可以用Java调用c/c++,或者使用c/c++调用Java

本篇文章只介绍jna的使用方法,不涉及原理部分。需要学习原理的,可以查看文章结尾链接或自行查找。

数据类型对应关系

使用jna调用c/c++函数时,最难的部分是搞清楚参数类型之间的对应关系。本文章主要将参数分为三部分,一类是普通类型,一类是指针类型,最后是输出参数。

普通类型对应关系如下:

java类型c类型原生表现bytechar8位整数charwchar_t平台依赖shortshort16位整数intint32位整数longlong long, __int6464位整数floatfloat32位浮点数doubledouble64位浮点数Buffer/Pointerpointer平台依赖(32或64位指针)Stringchar[]

指针类型需要用到jna 的Pointer类和PointerByReference类。Pointer类代表指向任何东西的指针,PointerByReference类表示指向指针的指针。Pointer类更加通用,事实上PointerByReference类内部也持有Pointer类的实例。

c类型jna类型void**PointerByReferencevoid*Pointerchar**PointerByReferencechar&PointerByReferencechar*Pointerint&IntByReferenceint*IntByReferencechar*Stringchar**String[]

部分函数涉及到输出参数,或者既是输入,也是输出,部分对照关系如下:

c类型jna类型intIntByReferencechar*byte[]

编码示例

c函数:

/** * \param[OUT] handle - 句柄 * \param[IN] name * \return 错误码 */ SA_API int SA_Create(void** handle, const char* name); /** * \brief 释放句柄,不在使用时请释放句柄 * \param[IN] hande - 需要释放的句柄 */ SA_API int SA_Delete(void** handle); /** * \param[IN] hande - 已初始化的句柄 * \param[IN] w_path * \param[IN] device * \param[IN] a_growth * \param[IN] r_threshold * \return 错误码 */ SA_API int SA_InitDetector(void* handle, const char* w_path, const char* device, char a_growth = 1, int i_format = 2, float r_threshold = 0.5f); /** * \param[IN] hande - 已初始化的句柄 * \param[IN] image * \param[IN] width * \param[OUT] result * \param[IN|OUT] result_length * \return 错误码 */ SA_API int SA_DetectAndRecognize(void* handle, const unsigned char* image, int width, char* result, int* result_length);

jna

jna代码主要分为两部分,一个是接口文件,一个是具体的类。 接口定义:

public interface Steel extends Library{ Steel INSTANCE = (Steel) Native.loadLibrary("d:\\SA_Steel.dll", Steel.class); /** * \brief 创建句柄,调用以下任意函数前需要先创建句柄 * \param[OUT] handle - 句柄 * \param[IN] name * \return 错误码 */ public int SA_Create(PointerByReference pHandle, String name); /** * \brief 释放句柄,不在使用时请释放句柄 * \param[IN] hande - 需要释放的句柄 */ int SA_Delete(PointerByReference pHandle); /** * \param[IN] hande - 已初始化的句柄 * \param[IN] w_path * \param[IN] device * \param[IN] a_growth * \param[IN] r_threshold * \return 错误码 */ int SA_InitDetector(Pointer Handle, String w_path, String device, int a_growth, float r_threshold ,); /** * \param[IN] hande - 已初始化的句柄 * \param[IN] image * \param[IN] width * \param[OUT] result * \param[IN|OUT] result_length * \return 错误码 */ int SA_DetectAndRecognize(Pointer handle, byte[] pImg, int width, byte[] result, IntByReference i); }

类:

public class Test{ public static void main(String[] args) { PointerByReference pHandle = new PointerByReference(); int ret= Steel.INSTANCE.SA_Create(pHandle, "R"); ret= Steel.INSTANCE.SA_InitDetector(pHandle.getValue(), "d://SA_3.dll", "GPU", 1, 0.4f); byte[] byteArr = "".getBytes(); byte[] pointerByReference=new byte[35]; IntByReference intByReference=new IntByReference(); intByReference.setValue(35); int width = 0; ret= Steel.INSTANCE.SA_DetectAndRecognize(pHandle.getValue(), byteArr, width, pointerByReference, intByReference); //举例,若需要接收输出类型的参数,以int为例,获取IntByReference或PointerByReference的值时,调用getValue()方法 IntByReference width=new IntByReference(); ret=Steel.INSTANCE.SA_LastDetectedRect(pHandle.getValue(),width); System.out.println("width:"+width.getValue()); Steel.INSTANCE.SA_Delete(pHandle); }

jna的jar包可以去阿里云的maven库下载,附地址maven仓库

参考文章:void**、void*、char**、char*、int*等类型映射关系及简单示例 参考文章:关于(void**)&的理解 参考文章:java通过jna调用dll文件 参考文章:JNA简介及使用 参考文章:深入浅出JNA-快速调用原生函数

最新回复(0)