JNA (Java 本地访问)理论概述与入门

目录

JNA (Java 本地访问)概述

C 语言 与 Java

JNA 入门示例

msvcrt.dll 运行库

printf 打印方法

kernel32 动态链接库

GetLogicalDriveStringsA

GetSystemDirectoryA


本文参考网址:https://blog.csdn.net/gwd1154978352/article/details/55097376

本文基于 win10 的 64 位操作系统

JNA (Java 本地访问)概述

1、JNA 全称 Java Native Access (Java 本地访问),JNA 提供一组 Java 工具类用于在运行期间动态访问系统本地库(native library:如Window的dll)而不需要编写任何Native/JNI代码。

2、开发人员只要在一个Java接口中描述目标native library的函数与结构,JNA将自动实现Java接口到native function的映射。

3、JNA全称Java Native Access,是一个建立在经典的JNI(Java Native Interface-Java本地接口)技术之上的Java开源框架

4、Github托管地址:https://github.com/java-native-access/jna

5、JNA 开发包下载地址

  1. https://maven.java.net/content/repositories/releases/net/java/dev/jna/jna/4.0.0/jna-4.0.0.jar

  2. https://maven.java.net/content/repositories/releases/net/java/dev/jna/jna-platform/4.0.0/jna-platform-4.0.0.jar

6、官方入门示例:https://github.com/java-native-access/jna/blob/master/www/GettingStarted.md

6、通俗的说就是使用 JNA 可以更方便的调用 windows/Linux 系统底层的函数(方法)

C 语言 与 Java

1、Windows 底层主要使用 C++ 编写以及还有 C 语言加汇编语言,Linux 主要以 C 语言为主

2、dll 和 so 是 C 函数的集合和容器,这与 Java 中的接口概念吻合,所以 JNA 把 dll 文件和 so 文件看成一个个接口。在 JNA 中定义一个接口就是相当于了定义一个 DLL/SO 文件的描述文件,该接口代表了动态链接库中发布的所有函数。而且,对于程序不需要的函数,可以不在接口中声明。

3、JNA 定义的接口一般继承 com.sun.jna.Library 接口,如果dll文件中的函数是以stdcall方式输出函数,那么该接口就应该继承com.sun.jna.win32.StdCallLibrary接口。

4、JNA 难点在于学 Java 的人完全不知道电脑系统底层到底有哪些库?库叫什么名字?库中有哪些函数?函数是什么含义?等等

Java 和 C 数据类型对照表

Java 类型

类型

原生表现

 

 boolean

 int

 32位整数(可定制)

 

 byte

 char 

 8位整数

 

 char

 wchar_t

 平台依赖

 

 short

 short

 16位整数

 

 int

 int

 32位整数

 

 long

long long, __int64

 64位整数

 

 float

 float

 32位浮点数

 

 double

 double

 64位浮点数

 

 Buffer/Pointer

 pointer

 平台依赖(3264位指针)

 

 <T>[] (基本类型的数组)

 pointer/array

3264位指针(参数/返回值)

邻接内存(结构体成员)

 

 String

 char*

/0结束的数组 (native encoding or jna.encoding)

 

 WString

 wchar_t*

 /0结束的数组(unicode)

 

 String[]

 char**

 /0结束的数组的数组

 

 WString[]

 wchar_t**

 /0结束的宽字符数组的数组

 

 Structure

 struct*/struct

指向结构体的指针(参数或返回值) (或者明确指定是结构体指针)结构体(结构体的成员) (或者明确指定是结构体)

 

 Union

union 

 等同于结构体

 

 Structure[]

 struct[]

 结构体的数组,邻接内存

 

 Callback

 <T> (*fp)()

 Java函数指针或原生函数指针

 

 NativeMapped

 varies

 依赖于定义

 

 NativeLong

 long

 平台依赖(3264位整数)

 

 PointerType

 pointer

 Pointer相同

JNA 入门示例

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;

/** Simple example of JNA interface mapping and usage. */
public class HelloWorld {
    // This is the standard, stable way of mapping, which supports extensive
    // customization and mapping of Java to native types.
    public interface CLibrary extends Library {
        CLibrary INSTANCE = (CLibrary) Native.loadLibrary((Platform.isWindows() ? "msvcrt" : "c"), CLibrary.class);
        void printf(String format, Object... args);
    }

    public static void main(String[] args) {
        CLibrary.INSTANCE.printf("Hello, World\n");
        for (int i=0;i < args.length;i++) {
            CLibrary.INSTANCE.printf("Argument %d: %s\n", i, args[i]);
        }
    }
}

1、运行程序,没带参数时只打印出“Hello, World”,带了参数时,则会打印出所有的参数。

2、没有写一行 C 代码,就直接在 Java 中调用了系统动态链接库中的函数!

程序说明:

一:自定义接口

public interface CLibrary extends Library {  .....

自定义一个接口,继承Library 或 StdCallLibrary。默认的是继承Library ,如果动态链接库里的函数是以stdcall方式输出的,那么就继承StdCallLibrary,比如众所周知的kernel32库

二:接口内部定义

Clibrary INSTANCE = (Clibrary) Native.loadLibrary((Platform.isWindows() ? "msvcrt" : "c"),  CLibrary.class);

接口内部需要一个公共静态常量:INSTANCE,通过这个常量,就可以获得这个接口的实例,从而使用接口的方法,也就是调用外部 dll/so 的函数

INSTANCE 常量通过 Native.loadLibrary() API函数获得,该函数有2个参数:

第一个参数是动态链接库dll/so的名称,但不带.dll或.so这样的后缀,这符合JNI的规范,因为带了后缀名就不可以跨操作系统平台了。搜索动态链接库路径的顺序是:先从当前类的当前文件夹找,如果没有找到,再在工程当前文件夹下面找win32/win64文件夹,找到后搜索对应的dll文件,如果找不到再到WINDOWS下面去搜索,再找不到就会抛异常了。比如上例中printf函数在Windows平台下所在的dll库名称是"msvcrt",而在其它平台如Linux下的so库名称是"c"。

第二个参数是自定义接口的Class类型。JNA通过这个Class类型,根据指定的.dll/.so文件,动态创建接口的实例。该实例由JNA通过反射自动生成。

void printf(String format, Object... args);  接口中只需要定义要用到的函数或者公共变量,不需要的可以不定义。注意参数和返回值类型,应该和系统链接库中的函数类型保持一致。

三:调用链接库函数

定义好接口后,就可以使用接口中的方法即相应dll/so中的函数了,通过接口中的实例即可进行调用

如:CLibrary.INSTANCE.printf("Hello, World\n");  即可调用系统中的printf函数

msvcrt.dll 运行库

1、msvcrt.dll 是微软在windows操作系统中提供的C语言运行库执行文件(Microsoft Visual C Runtime Library)

2、msvcrt.dll 中提供了printf,malloc,strcpy 等 C语言库函数的具体运行实现

printf 打印方法

1、第一步自定义接口继承Library,接口名称建议与库名称一致,如 Msvcrt。如果dll库文件中的函数是以stdcall方式输出,那么自定义接口就应该继承com.sun.jna.win32.StdCallLibrary接口。

2、使用 Native 加载链接库,msvcrt的后缀名.dll不要写,想要使用msvcrt.dll库中哪些方法,就在自定义接口声明它即可

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;
/**
 * Created by Administrator on 2018/6/27 0027.
 */
public interface Msvcrt extends Library {
    Msvcrt INSTANCE = (Msvcrt) Native.loadLibrary(
            (Platform.isWindows() ? "msvcrt" : "c"),
            Msvcrt.class
    );
    void printf(String format, Object... args);
}

3、第二步则是常见自定义接口的实例调用接口中的方法,也是msvcrt库中的函数

/**
 * 测试msvcrt.dll的printf方法
 */
@Test
public void test1() {
    Msvcrt msvcrt = Msvcrt.INSTANCE;
    msvcrt.printf("Hello JNA");
}

kernel32 动态链接库

1、kernel32.dll 是Windows中非常重要的32位动态链接库文件,属于内核级文件。

2、kernel32.dll 控制着系统的内存管理、数据的输入输出操作和中断处理,当Windows启动时,kernel32.dll就驻留在内存中特定的写保护区域,使别的程序无法占用这个内存区域。

3、kernel32.dll库中有GetLogicalDriveStringsA、GetSystemDirectoryA等方法

GetLogicalDriveStringsA

1、获取本地系统的系统盘符

2、自定义接口,然后指明动态链接库名称,同时声明方法

import com.sun.jna.Library;
import com.sun.jna.Native;
/**
 * Created by Administrator on 2018/6/27 0027.
 */
public interface Kernel32 extends Library {
    Kernel32 INSTANCE = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class);

    /**
     * 【获取本地系统逻辑盘符】
     * @param length
     * @param buffer
     * @return
     */
    int GetLogicalDriveStringsA(int length,byte[] buffer);
}
/**
 * 测试kernel32.dll的GetLogicalDriveStringsA方法
 * 【获取本地系统逻辑盘符】
 */
@Test
public void test2() {
    Kernel32 kernel32 = Kernel32.INSTANCE;

    byte[] buffer2 = new byte[64];
    /**
     * GetLogicalDriveStringsA方法会把结果缓存到buffer2字节数组中
     * logicalDriveStringsSize:方法返回值其实是结果的大小
     */
    int logicalDriveStringsSize = kernel32.GetLogicalDriveStringsA(buffer2.length / 2, buffer2);
    StringBuilder stringBuilder = new StringBuilder(logicalDriveStringsSize);
    for (byte bt : buffer2) {
        stringBuilder.append((char) bt);
    }
    String logicalDriveStrings = stringBuilder.toString().trim();
    System.out.println(logicalDriveStrings);
}

GetSystemDirectoryA

import com.sun.jna.Library;
import com.sun.jna.Native;
/**
 * Created by Administrator on 2018/6/27 0027.
 */
public interface Kernel32 extends Library {
    Kernel32 INSTANCE = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class);
    /**
     * 【获取本地系统逻辑盘符】
     * @param length
     * @param buffer
     * @return
     */
    int GetLogicalDriveStringsA(int length,byte[] buffer);

    /**
     * 【获取系统目录】
     * @param buffer
     * @param size
     * @return
     */
    int GetSystemDirectoryA(byte[] buffer,int size);
}
/**
 * 测试kernel32.dll的GetSystemDirectoryA方法
 * 【获取系统目录】
 */
@Test
public void test3() {
    Kernel32 kernel32 = Kernel32.INSTANCE;
    byte[] buffer = new byte[50];
    /**
     * 同理此方法也是将结果缓存到了字节数组中,数组大小自己设置即可
     * 返回值systemDirectoryStrSize仍然是结果的长度大小
     */
    int systemDirectoryStrSize = kernel32.GetSystemDirectoryA(buffer, 50);
    StringBuilder stringBuilder = new StringBuilder(systemDirectoryStrSize);
    for (byte bt : buffer) {
        stringBuilder.append((char) bt);
    }
    String systemDirectoryStr = stringBuilder.toString().trim();
    System.out.println(systemDirectoryStr);
}

 

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蚩尤后裔-汪茂雄

芝兰生于深林,不以无人而不芳。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值