原文:Simplify Native Code Access with JNA
作者:Sanjay Dasgupta
出处:http://today.java.net/article/2009/11/11/simplify-native-code-access-jna
本文介绍了把本地代码库(native library)和Java编程集成在一起的Java本地访问(Java Native Access,JNA)方法,展示了在不需要使用另一种语言编写的粘接代码的情况下,JNA是如何使Java代码调用本地函数成为可能的。文中的一些例子说明了使用模式、易犯错误以及疑难排解技术。本文还通过描述来自较早的一篇java.net文章中的JNI例子代码到JNA的转换来对JNA和JNI(Java Native Interface)做了比较。
了解JNA是很有用处的,因为看重架构中立的Java API是从不支持平台特定的功能的。因此,举例来说,如果你刚刚发明的某一killer应用程序需要播放Windows的“紧急停止”声响的话,那么你就会被卡在这个地方,因为不能借助标准的API来调用Windows的MessageBeep()函数。
虽然Java本身是架构中立的,但在本文中使用的例子代码却是、也必然是平台特定的。这些代码是在运行了32位Microsoft Windows XP和Sun JRE 1.6.0 update 16的笔记本电脑上进行开发和测试的,不过,这些代码是非常通用的,应该可以在一系列的Windows和JVM版本上运行,代码还未用到Java 1.6、Windows 2008和Windows Vista的新功能。
进行JNA开发的第一步
在开始一个JNA工程的时候,必须要进行以下几件事情:
从JNA项目网站下载jna.jar,并把它添加到工程的构建路径中,该文件是唯一需要的JNA资源,需要记住的是,jna.jar也必须被包含到运行时的类路径中。
找出Java代码将要访问的DLL的名称,DLL名称需要用来初始化JNA的联接机制。
创建用来表示应用将要访问的DLL的Java接口,本文附带的示例代码包含了三个DLL:kernel32.dll、user32.dll和Twain_32.dll的接口例子。
测试Java代码到本地方法的联接,后面的第一个例子“联接:名称的作用”描述了当JNA不能找到DLL或者DLL中的函数时,预期抛出的异常。
如果工程很大或者很复杂的话,那么在早期阶段就完成这些步骤会是一个好主意。如果需要进行概念验证(proof of concept,POC)的话,那么可以考虑在POC中包含JNA接口代码的重要部分,这有助于验证关于JNA是否适合此项工作的假设,并会降低整个工程的风险。
DLL的代理
JNA使用代理模式来隐藏本地代码集成的复杂性,它提供了一个工厂方法,Java程序使用该方法来获取DLL的代理对象,然后程序就可以通过调用代理对象相应的方法来调用DLL的函数,下面图1中的序列图描述了代理对象的创建和使用。
图1:创建DLL的Java代理对象
JNA包办了运行时的各个方面,但需要你帮助创建代理的Java类,因此你需要创建的第一段代码是带有方法定义的Java接口,这些方法定义需要和DLL的函数相一致。若要正确地与JNA运行时配合的话,接口必须扩展com.sun.jna.Library,以下的代码展示了Windows user32 DLL的代理接口的一个简略形式,需要注意的是,应该为每个DLL都提供一个这样的Java接口。
package libs;
import com.sun.jna.win32.Library;
public interface User32 extends Library {
... (为了清晰删除的一些行) ...
boolean LockWorkStation();
boolean MessageBeep(int uType);
... (为了清晰删除的一些行) ...
}
|
许多DLL,例如Windows API中的那些,拥有众多的函数,但代理接口只需要包含应用真正用到的那些方法的声明就可以了。
联接:名称的作用
我们的第一个例子(LockWorkStation.java)非常的简单,就是在工作站运行时锁定它(与同时按下Windows的标志键和L键的效果是一样的)。它使用之前展示的User32接口来创建Windows user32 DLL的代理,然后调用代理的LockWorkStation()方法——该方法接着调用DLL的LockWorkStation()函数,JNA透明地处理了代理方法到DLL函数的运行时映射——用户只需要确保方法名称和函数名称完全相同。
import com.sun.jna.Native; // JNA基础构建
import libs.User32; // user32.dll的代理接口
public class LockWorkStation {
public static void main(String[] args) {
// 创建user32.dll的代理…...
User32 user32 = (User32) Native.loadLibrary("user32", User32.class);
// 借助代理调用“LockWorkStation()”…...
user32.LockWorkStation();
}
}
|
按照后面“运行示例代码”一节中的指引来编译和运行此程序。
LockWorkStation()调用无需参数和返回值,这消除了任何编程错误的可能性,但有两件事情仍然能够很简单地导致代码出错,情况如下:
loadLibrary()抛出信息为“Unable to load library ... The specified module could not be found.”的java.lang.UnsatisfiedLinkError异常,如果这一错误出现的话,检查一下DLL名称的拼写,以及验证一下DLL是否存在于搜索目录中(查看JNA文档)。
代理方法(例如LockWorkStation())抛出信息为“Error looking up function ... The specified procedure could not be found.”的java.lang.UnsatisfiedLinkError异常,如果这一错误出现的话,检查一下函数名称的拼写,以及验证一下该“函数”实际上并非一个宏(macro),Windows API DLL包含了不少的这样的宏(例如Winuser.h中定义的GetMessage()),因此,仔细阅读一下DLL的文档(以及相应的头文件),宏的名称需要人工进行转换。
在运行LockWorkStation.java的时候应该不会收到任何一个这样的异常,不过只要修改DLL的名称或者函数的名称然后重新编译代码,你就可以模拟这些错误。事实上,JNA确实有允许使用与函数名称(在DLL中)不同的方法名称(在代理接口中)的机制,关于这一功能的更多信息可以在JNA文档中找到。
参数和返回类型
下面展示了我们的下一个例子,使用Windows的Beep()函数来逐字地发出代表“Hello world”的摩尔斯电码的声音。
import com.sun.jna.Native; // JNA基础构建
import libs.Kernel32; // kernel32.dll的代理接口
public class BeepMorse {
private static Kernel32 kernel32 = (Kernel32)
Native.loadLibrary("kernel32", Kernel32.class);
private static void toMorseCode(String letter) throws Exception {
for (byte b : letter.getBytes()) {
kernel32.Beep(1200, ((b == '.') ? 50 : 150));
Thread.sleep(50);
}
}
public static void main(String[] args) throws Exception {
String helloWorld[][] = {
{"....", ".", ".-..", ".-..", "---"}, // HELLO
{".--", "---", ".-.", ".-..", "-.."} // WORLD
};
for (String word[] : helloWorld) {
for (String letter : word) {
toMorseCode(letter);
Thread.sleep(150);
}
Thread.sleep(350);
}
}
}
|
Beep()函数有两个参数:频率和持续时间,这两个类型为DWORD的值被定义为无符号的长整型,由于当前所有的Windows系统中的无符号长整型都是32位长的,因此我们在代理接口中使用Java的int类型来定义这两个参数,如下所示:
package libs;
import com.sun.jna.Library;
public interface Kernel32 extends Library {
// ... (为了清晰删除的一些行) ...
boolean Beep(int frequency, int duration);
int GetLogicalDrives();
// ... (为了清晰删除的一些行) ...
}
|
给出正确的参数类型非常重要,你可以通过修改Beep()的参数类型来验证这一点,把定义修改成Beep(long, long)或者Beep(float, float)不会引起任何的运行时错误,但是你会完全听不到任何声音。JNA的网站上有一些关于把Windows类型转换成Java类型的资料,更多详细的信息则可以在维基百科的Windows Programming/Handles and Data Types页面和Microsoft的Windows Data Types网页中找到。
按照后面“运行示例代码”一节中的指引来编译和运行这一程序,不过记得要先把音量调低!
Beep()返回一个布尔值,虽然在这个例子中它被忽略了,不过如果函数返回的值是有用的话,那么必须使用与参数类型相同的准则把返回的类型映射到一个合适的Java类型上。下面的代码(GetLogicalDrives.java)说明了由kernel32 DLL中的GetLogicalDrives()函数返回的int值的使用情况。
import com.sun.jna.Native;
import libs.Kernel32;
public class GetLogicalDrives {
public static void main(String[] args) {
Kernel32 kernel32 = (Kernel32)
Native.loadLibrary("kernel32", Kernel32.class);
int drives = kernel32.GetLogicalDrives();
for (int i = 0; i < 32; ++i) {
int bit = (1 << i);
if ((drives & bit) == 0)
continue;
System.out.printf("%c:\\%n", (char) ((int) 'A' + i));
}
}
}
|
不过需要注意的是,在现实情况中,Java程序应该从不需要使用JNA来调用GetLogicalDrives(),因为java.io.File.listRoots()提供了同样的信息。
按照后面“运行示例代码”一节中的指引来编译和运行该程序。
本文的引言部分提到了使用Windows的标准声响来指示具体的事件,这些声响可以通过调用MessageBeep(int type)函数来产生,可以在文件MessageBeep.java中找到展示使用MessageBeep()的示例代码。
[译文] 使用JNA来简化对本地代码的访问[二]
[译文] 使用JNA来简化对本地代码的访问[三]
- 大小: 8 KB
- 大小: 5.3 KB
- 大小: 11.8 KB
分享到:
相关推荐
与Java Native Interface(JNI)相比,JNA提供了一种更简单、更安全的方法来调用本地方法,无需编写额外的本地代码包装器或使用Java Native Header(JNH)文件。本文将详细介绍JNA的基本概念、工作原理、使用方式...
本篇文章将详细讲解如何在Android中使用JNA来调用C/C++方法。 首先,了解JNA的基本概念。JNA是一种轻量级的Java库,它允许Java程序直接调用动态链接库(DLL)或共享库(SO)中的函数,无需编写JNI代码。相比之下,...
1. **工具介绍**:JNAERA(可能是JNA Easier Rapid Access的缩写)是一个基于JNA的代码生成工具,旨在简化与本地库的集成过程。它通过分析C/C++头文件,自动生成对应的Java接口代码。 2. **安装与配置**:文章可能...
详细演示了如何通过JNA实现以下形式的动态链接库接口: 1:基本数据类型 2:基本数据类型的指针和引用 3:结构体 4:结构体的指针和引用 5:函数指针和回调函数 6:字符串指针 7:输入一个数组 8:输出一个数组并...
总的来说,JNA为Java开发者提供了一种强大而灵活的方式来与本地代码交互,使得Java应用能够充分利用操作系统提供的各种功能,而无需编写或管理本地代码。无论你是想进行系统级编程、调用已有的C/C++库还是操作硬件...
Java Native Access(JNA)是Java平台上的一个开源库,它允许Java代码直接与本地操作系统API进行交互,而无需编写JNI(Java Native Interface)代码。JNA通过动态加载库并映射函数,使得Java程序员可以方便地调用C、...
JNA通过一个小型的JNI库来实现对本地代码的动态调用。其主要特点包括: - 开发者仅需通过Java接口描述目标本地库的功能和结构,从而轻松利用本机平台的功能,而无需为多平台配置和生成JNI代码付出高昂的成本。 - JNA...
该资源中,包含jna.jar 和一个jna的函数文档。...该例子是访问本地的IC卡读卡器,其中因为原始厂商提供的本地代码函数过于发杂,为了便于JNA调用,又对本地代码进行了封装。这是一个很好的JNA使用范例。
8. **性能**:尽管JNA相比JNI可能稍慢,但其易用性和简洁性使得它成为许多项目首选的原生访问方式,特别是在需要快速原型开发或者对性能要求不那么高的情况下。 9. **错误处理**:JNA通过抛出`LastErrorException`...
总的来说,JNA是Java程序员调用本地库的一个强大工具,尤其适用于那些对性能要求不是特别高,但又希望避免JNI复杂性的项目。不过,对于需要高性能交互或者处理复杂数据结构的情况,JNI可能是更好的选择。在实际项目...
JNA通过映射Java方法到本地函数,实现了Java与操作系统底层功能的交互,极大地简化了在Java应用中使用系统API或DLL库的过程。在这个场景中,我们有两个文件:`jna-3.3.0.jar`和`jna-3.3.0-platform.jar`,它们分别是...
它提供已描述好本地函数类型的一组工具接口来简化本地访问. JNA与JNI比较, 它不需要生成DLL, JNA基于JNI技术, 简化了JAVA本地访问的过程, 但性能不如JNI. 从之前发布其他chm文件下载用户的反映看,有不少朋友反映...
总之,JNA是Java开发者访问本地资源的重要工具,它简化了Java与本地代码的交互过程,使得Java应用能够充分利用操作系统提供的功能。然而,使用时需要注意性能和类型兼容性的问题,合理选择何时使用JNA,何时编写JNI...
Java Native Access (JNA) 是一个Java库,它允许Java代码与本地库进行交互,无需编写C语言的JNI(Java Native Interface)代码。这个技术极大地简化了Java应用程序调用操作系统功能或者使用非Java编写的库的过程。在...
总的来说,JNA作为一个强大的库,简化了Java与本地库交互的过程,使得开发者可以在Java环境中充分利用系统资源,实现更丰富的功能。在使用过程中,需要注意版本间的差异,以及正确配置和管理本地库的依赖。同时,...
JNA(Java Native Access )提供一组Java工具类用于在运行期动态访问系统本地库(native library:如Window的dll)而不需要编写任何Native/JNI代码。开发人员只要在一个java接口中描述目标native library的函数与...
Java Native Access(JNA)是Java平台上的一个开源库,它允许Java代码与本地操作系统功能进行交互,无需编写C语言的动态链接库或Java Native Interface(JNI)代码。JNA提供了一种简单的方法来调用操作系统API,以及...
JNA(Java Native Access)是一个开源项目,它提供了一种方便的方式来调用本地库(如C、C++编写的动态链接库或静态库)而无需编写JNI(Java Native Interface)代码。JNA通过一个自动生成的映射层,使得Java开发者...
9. **JNA的局限性**:虽然JNA简化了跨语言调用,但它并不适合所有情况,特别是需要高性能或复杂的本地代码交互时,JNI可能仍然是更好的选择。 10. **使用JNA的步骤**:创建本地函数接口,映射Java类型,加载动态...
JNA提供了一种无需编写C代码或使用JNI(Java Native Interface)即可调用操作系统API的方法。本话题将详细介绍如何使用JNA获取窗口句柄并发送消息,以及在CS(Client-Server)架构中实现SSO(Single Sign-On)。 ...