JNA-JNI升级版

一、概述

JNA(Java Native Acess)框架是一个开源的Java框架,是SUN公司主导开发的,建立在经典的JNI的基础之上的一个框架,  功能强大,易用,类似于.NET的P/Invoke. 我们知道, 使用JNI调用.dll/.so共享类库是非常麻烦和痛苦的.如果有一个现有的.dll/.so文件, 如果使用JNI技术调用, 我们首先需要另外使用C语言写一个.dll/.so共享库,使用SUN规定的数据结构替代C语言的数据结构,调用已有的dll/so中公布的函数. 然后再在Java中载入这个适配器dll/so, 再编写Java Native函数作为dll函数中的代理. 经过2个繁琐步骤才能在Java中调用本地代码. 因此, 很少有Java程序员愿意编写dll/so库中的原生函数的Java程序, 这也使Java语言在客户端上乏善可陈. 可以说JNI是Java的一大弱点!

二、P/Invoke和JNA

而在.NET平台上,强大的P/Invoke技术使我们Java程序员非常羡慕. 使用P/Inovke技术, 只需要使用编写一个.NET函数,再加上一个声明的标注, 就可以直接调用dll中的函数. 不需要你再使用C语言编写dll来适配.

现在, 不需要再羡慕.NET的P/Invoke机制了, JNA把对dll/so共享库的调用减少到了和P/Invoke相同的程度. 使用JNA, 不需要再编写适配用的.dll/.so, 只需要在Java中编写一个接口和一些代码, 作为.dll/.so的代理, 就可以在Java程序中调用dll/so.

三、快速启动

下载一个jar包,就可以使用JNA的强大功能方便地调用动态链接库中的C函数. 示例1:

import com.sun.jna.Library;

import com.sun.jna.Native;

import com.sun.jna.Platform;

public class HelloWorld {

public interface CLibrary extends Library {

String libraryName = Platform.isWindows() ? "msvcrt" : "c";

CLibrary INSTANCE = (CLibrary)Native.loadLibrary(libraryName, 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]);

}

}

}

执行以后, 可以看到输出内容

D:\MyStudy\Java\mycode>java -cp jna.jar;. HelloWorld test adfa lkj

Hello World

Argument 0 : test

Argument 1 : adfa

Argument 2 : lkj

这里,程序的打印输出实际上是使用了msvcrt.dll这个C运行时库中的printf函数打印出上面的内容, 不需要写C代码就可以直接在Java中调用外部动态链接库中函数了!

示例2:

1,在VS中选择C++语言,然后选择创建一个Win32程序。 选择dll类型。

2,发布的C函数是:

#define MYLIBAPI extern "C" __declspec(dllexport) 

MYLIBAPI void say(wchar_t* pValue);

//这个函数的实现是:

void say(wchar_t* pValue){

    std::wcout.imbue(std::locale("chs"));

    std::wcout<<L"DLL程序说:"<<pValue<<std::endl;

}// 它需要传入一个Unicode编码的字符数组。然后在控制台上打印出一段中文字符。

 3,生成dll。然后把生成的dll文件复制到Eclipse项目中,放在项目下面。

4,在Eclipse中编写以下代码:

import com.sun.jna.Library;

import com.sun.jna.Native;

import com.sun.jna.WString;

public class TestDll1Service {

         public interface TestDll1 extends Library {

                   /**

                    * 当前路径是在项目下,而不是bin输出目录下。

                    */

                   TestDll1 INSTANCE = (TestDll1)Native.loadLibrary("TestDll1", TestDll1.class);

                   public void say(WString value);

         }

         /**

          * @param args

          */

         public static void main(String[] args) {

                   // TODO Auto-generated method stub                 

                   TestDll1.INSTANCE.say(new WString("这段字符串来自JAVA程序!"));

                   System.out.println("这段话是由Java直接打印出来的.");

         }

}

5,执行这个Java类。可以看到控制台下如下输出:

DLL程序说:这段字符串来自JAVA程序!

这段话是由Java直接打印出来的.

6,上面一行是C语言使用C++的std::wcout输出的。

下面一行是Java语言输出的。