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语言输出的。