JNA调用C动态库dll、so

来源:互联网 发布:php 防止sql注入 编辑:程序博客网 时间:2024/04/28 07:49

1.介绍jna

          JNA(Java Native Access )提供一组Java工具类用于在运行期动态访问系统本地库(native library:如Window的dll)而不需要编写任何Native/JNI代码。开发人员只要在一个java接口中描述目标native library的函数与结构,JNA将自动实现Java接口到native function的映射。

优点

         JNA可以让你像调用一般java方法一样直接调用本地方法。就和直接执行本地方法差不多,而且调用本地方法还不用额外的其他处理或者配置什么的,也不需要多余的引用或者编码,使用很方便。

与jni对比          

         在java JDk也有调用动态库的技术:jni,相对jna来说,jna功能给强大,效率更高,但是入门门槛高,它要求调用的函数库是符合jni调用规范的。如果你要调用的函数库不符合jni规范,对不起,你得自己用C写一个符合jni调用规范的函数库,在里面调用要你要使用的函数,然后你再去调用这个jni规范的函数库,实现你的功能。这样的话开发量更大,而且开发难度也更大(要求你会C),但使用jna的话就问题就简单很多了,我不需要你的函数符合jni规范,直接可以调用原生函数库,不需要编写任何Native/JNI代码。

使用jna(jni)的两个缺点:

1、程序不再跨平台。要想跨平台,必须在不同的系统环境下重新编译本地语言部分。
2、程序不再是绝对安全的,本地代码的不当使用可能导致整个程序崩溃。一个通用规则是,你应该让本地方法集中在少数几个类当中。这样就降低了JAVA和C之间的耦合性。
 

2.与java数据类型的映射

跨平台、跨语言调用的最大难点,就是不同语言之间数据类型不一致造成的问题。绝大部分跨平台调用的失败,都是这个问题造成的。

JNA使用的数据类型是Java的数据类型。而原生函数中使用的数据类型是原生函数的编程语言使用的数据类型。可能是C,Delphi,汇编等语言的数据类型。因此,不一致是在所难免的。

JNA提供了Java和原生代码的类型映射。

和操作系统数据类型的对应表

Java 类型

C 类型

原生表现

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

平台依赖(32或 64位指针)

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

pointer
array

32或 64位指针(参数/返回值)
邻接内存(结构体成员)

支持常见的数据类型的映射

Java 类型

C 类型

原生表现

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

平台依赖(32或64位整数)

PointerType

pointer

Pointer相同

 

尽量使用基本、简单的数据类型;

尽量少跨平台、跨语言传递数据!

3.使用

dll、so函数库区别

dll、so函数库都可以通过jna来调用,dll与so函数库的区别是:dll是C编译windows环境下使用的函数库,而so是在linux等环境下使用的函数库,他们调用的方法都是一样的。

下面是一个调用dll的例子:

函数库libHsMacAPI.h头文件(相对于函数库api)

 函数名称:  GenZAK 功能描述:  用于生成MACKey 参数说明:  sMacKey 存放生成的MACKEY,长度为16位   返回值:  无 void GenZAK(char * sMacKey); ********************************************************************* 函数名称:  GenMAC 功能描述:  用于生成MAC 参数说明:       sMacBuf   需要进行MAC的数据流     sMac      生成的MAC,最小为16字节     sMacKey   对MAC加密密钥(加密过的KEY)   返回值:  无 ********************************************************************** void GenMAC(char * sMacBuf, char * sMac, char * sMacKey); 函数名称:  MACVerify 功能描述:  用于检查MAC 参数说明:          sMacBuf   需要进行MAC的数据流        sMac     生成的MAC,最小为16字节        sMacKey   对MAC加密密钥(加密过的KEY)   返回值:         =0:校验成功      !=0: 校验不成功 ********************************************************************** int MACVerify(char * sMacBuf, char * sMac, char * sMacKey);

分析:

通过头文件我们可以看到:

1.在GenZAK方法中sMacKey是出参,相当于指针(Pointer),我们将一个指针传入,然后它给我们给我们的指针赋值,在java中我们使用对应的Pointer的子类Memory进行传值。

2.在GenMAC方法中sMacBuf 是入参,char*对应的是java中的String;sMac是出参,对应Memory,sMacKey是入参;对应java中是String,但我们之前定义了为Memory类型,这里我们就不将其转化为String了,还是直接用Memory类型。

3.在MACVerify方法中sMacBuf、sMacKey、sMac都是入参,因为我们之前定义sMacKey、sMac都是Memory类型,所以还是使用Memory类型,sMacBuf还是String类型。

入参使用与java对应的类型即可,出参都需使用指针类。

实现:

需要下载jna架包

1.编写jna接口代码(接口里面的方法名要跟C的接口方法名一致)

<p>package demo;</p><p>import com.sun.jna.Library;import com.sun.jna.Native;import com.sun.jna.Pointer;</p><p>/** * 接口里面的方法名要跟C的接口方法名一致 * 调用linux so(动态库) 获取mac  * @since JDK1.6 */public interface LibHsMacAPIJNA extends Library{ /*  然后开始我们的java接口 loadLibrary第一个参数就是你的dll名字 第二个就是当前接口的.class类型 */ //线程安全  (加载lib使用了绝对路径   切割去掉了路径前面的"/") public  LibHsMacAPIJNA INATANCE = (LibHsMacAPIJNA) Native.synchronizedLibrary((LibHsMacAPIJNA)Native.loadLibrary(LibHsMacAPIJNA.class.getResource("/libHsMacAPI.dll").getPath().substring(1), LibHsMacAPIJNA.class));  </p><p> /********************************************************************* 函数名称:  GenMAC 功能描述:  用于生成MAC 参数说明:          sMacBuf   需要进行MAC的数据流     sMac      生成的MAC,最小为16字节     sMacKey   对MAC加密密钥(加密过的KEY)   返回值:  无 **********************************************************************/ public void GenMAC(String sMacBuf, Pointer sMac, String sMacKey); /* 函数名称:  GenZAK 功能描述:  用于生成MACKey 参数说明:  sMacKey 存放生成的MACKEY,长度为16位   返回值:  无 **********************************************************************/ public void GenZAK(Pointer sMacKey); /********************************************************************* 函数名称:  MACVerify 功能描述:  用于检查MAC 参数说明:          sMacBuf   需要进行MAC的数据流     sMac      生成的MAC,最小为16字节     sMacKey   对MAC加密密钥(加密过的KEY)   返回值:         =0:校验成功   !=0: 校验不成功 **********************************************************************/ public int MACVerify(String sMacBuf, String sMac, String sMacKey);}</p>

Native.loadLibrary()加载函数库文件时,可以直接填写函数库名称(前缀lib可以去掉,后缀也可以去掉),不过前提是这个函数库必须放在系统或者java的函数库目录下;也可以放在任意位置,使用绝对路径。

2.调用函数库代码

package demo;import com.sun.jna.Memory;import com.sun.jna.Pointer;/** * 此类功能描述 * @version 2015-6-1 下午4:13:03 * @since JDK1.6 */public class LibHsMacAPITest {public static void main(String[] args) throws Exception {LibHsMacAPITest.generateMAC();}    /**     * 生成MAC     * @param method     * @param databuf     * @param datalen     * @param key     * @return     * @throws Exception     */    public static void generateMAC() throws Exception{      //获取key    Pointer sMacKey = new Memory(100);    LibHsMacAPIJNA.INATANCE.GenZAK(sMacKey);    String key = new String(sMacKey.getByteArray(0, 100));    System.out.println("key:" + key);    //加密    Pointer sMac = new Memory(16);    String sMacBuf = new String("1111");    LibHsMacAPIJNA.INATANCE.GenMAC(sMacBuf, sMac, key);    String mac = new String(sMac.getByteArray(0, 16),"utf-8");    System.out.println(mac);    //检验加密是否正确    int flag = LibHsMacAPIJNA.INATANCE.MACVerify(sMacBuf, mac, key);    System.out.println("检验结果:" + (flag == 0 ? "成功":"失败"));    }}

 

4.使用jna注意

 

注意事项

      在非web工程时,将需要调用的函数库文件放置在工程目录下是OK的;但在web工程中,放置在工程目录下是no OK的,因为项目发布时会将函数库也进行编译,造成函数库损坏。故在工程中使用jna,建议放在外部文件中,别放工程目录下。

     加载函数库时,使用的绝对路径中不能有中文!


工程地址:http://download.csdn.net/detail/u010722643/8893181

0 0
原创粉丝点击