《Java编程思想第4版[中文版](PDF格式)》第279章


代码。
A。3 J/Direct
J/Direct 是调用 Win32 DLL 函数最简单的方式。它的主要设计目标是与Win32API 打交道,但完全可用它调 
用其他任何 API。但是,尽管这一特性非常方便,但它同时也造成了某些限制,且降低了性能(与RNI 相 
比)。但J/Direct 也有一些明显的优点。首先,除希望调用的那个DLL 里的代码之外,没有必要再编写额外 
的非Java 代码,换言之,我们不需要一个封装器或者代理/存根DLL。其次,函数自变量与标准数据类型之 
间实现了自动转换。若必须传递用户自定义的数据类型,那么 J/Direct 可能不按我们的希望工作。第三,就 
象下例展示的那样,它非常简单和直接。只需少数几行,这个例子便能调用Win32 API 函数MessageBox(), 
它能弹出一个小的模态窗口,并带有一个标题、一条消息、一个可选的图标以及几个按钮。
public class ShowMsgBox {
public static void main(String args'')
throws UnsatisfiedLinkError {
MessageBox(0;
〃Created by the MessageBox() Win32 func〃;
〃Thinking in Java〃; 0);

655 
…………………………………………………………Page 657……………………………………………………………
/** @dll。import(〃USER32〃) */
private static native int
MessageBox(int hwndOwner; String text;
String title; int fuStyle);

令人震惊的是,这里便是我们利用 J/Direct 调用Win32 DLL 函数所需的全部代码。其中的关键是位于示范代 
码底部的MessageBox()声明之前的@dll 。import 引导命令。它表面上看是一条注释,但实际并非如此。它的 
作用是告诉编译器:引导命令下面的函数是在 USER32 DLL 里实现的,而且应相应地调用。我们要做的全部事 
情就是提供与DLL 内实现的函数相符的一个原型,并调用函数。但是毋需在Java 版本里手工键入需要的每一 
个Win32 API 函数,一个Microsoft Java 包会帮我们做这件事情(很快就会详细解释)。为了让这个例子正 
常工作,函数必须“按名称”由DLL 导出。但是,也可以用@dll。import 引导命令“按顺序”链接。举个例 
子来说,我们可指定函数在DLL 里的入口位置。稍后还会具体讲述@dll。import 引导命令的特性。
用非Java 代码进行链接的一个重要问题就是函数参数的自动配置。正如大家看到的那样,MessageBox()的 
Java 声明采用了两个字串自变量,但原来的C 方案则采用了两个 char 指针。编译器会帮助我们自动转换标 
准数据类型,同时遵照本章后一节要讲述的规则。
最好,大家或许已注意到了main()声明中的 UnsatisfiedLinkError 异常。在运行期的时候,一旦链接程序 
不能从非Java 函数里解析出符号,就会触发这一异常(事件)。这可能是由多方面的原因造成的:。dll 文 
件未找到;不是一个有效的DLL;或者J/Direct 未获您所使用的虚拟机的支持。为了使DLL 能被找到,它必 
须位于Windows 或WindowsSystem 目录下,位于由PATH 环境变量列出的一个目录中,或者位于和。class 文 
件相同的目录。J/Direct 获得了 Microsoft Java 编译器 1。02。4213 版本及更高版本的支持,也获得了 
Microsoft JVM 4。79。2164 及更高版本的支持。为了解自己编译器的版本号,请在命令行下运行 JVC,不要加 
任何参数。为了解 JVM 的版本号,请找到msjava。dll 的图标,并利用右键弹出菜单观察它的属性。
A。3。1 @dll。import 引导命令
作为使用J/Direct 唯一的途径,@dll。import 引导命令相当灵活。它提供了为数众多的修改符,可用它们自 
定义同非Java 代码建立链接关系的方式。它亦可应用于类内的一些方法,或应用于整个类。也就是说,我们 
在那个类内声明的所有方法都是在相同的 DLL 里实现的。下面让我们具体研究一下这些特性。
1。 别名处理和按顺序链接
为了使@dll。import 引导命令能象上面显示的那样工作,DLL 内的函数必须按名字导出。然而,我们有时想使 
用与DLL 里原始名字不同的一个名字(别名处理),否则函数就可能按编号(比如按顺序)导出,而不是按 
名字导出。下面这个例子声明了FinestraDiMessaggio() (用意大利语说的“MessageBox”)。正如大家看 
到的那样,使用的语法是非常简单的。
public class Aliasing {
public static void main(String args'')
throws UnsatisfiedLinkError {
FinestraDiMessaggio(0;
〃Created by the MessageBox() Win32 func〃;
〃Thinking in Java〃; 0);

/** @dll。import(〃USER32〃;
entrypoint=〃MessageBox〃) */
private static native int
FinestraDiMessaggio(int hwndOwner; String text;
String title; int fuStyle);

下面这个例子展示了如何同DLL 里并非按名字导出的一个函数建立链接,那个函数事实是按它们在DLL 里的 
位置导出的。这个例子假设有一个名为MYMATH 的DLL,这个 DLL 在位置编号 3 处包含了一个函数。那个函数 
获取两个整数作为自变量,并返回两个整数的和。
656 
…………………………………………………………Page 658……………………………………………………………
public class ByOrdinal {
public static void main(String args'')
throws UnsatisfiedLinkError {
int j=3; k=9;
System。out。println(〃Result of DLL function:〃
+ Add(j;k));

/** @dll。import(〃MYMATH〃; entrypoint = 〃#3〃) */
private static native int Add(int op1;int op2);

可以看出,唯一的差异就是entrypoint 自变量的形式。
2。 将@dll。import 应用于整个类
@dll。import 引导命令可应用于整个类。也就是说,那个类的所有方法都是在相同的DLL 里实现的,并具有 
相同的链接属性。引导命令不会由子类继承;考虑到这个原因,而且由于 DLL 里的函数是自然的 static 函 
数,所以更佳的设计方案是将API 函数封装到一个独立的类里,如下所示:
/** @dll。import(〃USER32〃) */
class MyUser32Access {
public static native int
MessageBox(int hwndOwner; String text;
String title; int fuStyle);
public native static boolean
MessageBeep(int uType);

public class WholeClass {
public static void main(String args'')
throws UnsatisfiedLinkError {
MyUser32Access。MessageBeep(4);
MyUser32Access。MessageBox(0;
〃Created by the MessageBox() Win32 func〃;
〃Thinking in Java〃; 0);


由于MessageBeep()和 MessageBox()函数已在不同的类里被声明成 static 函数,所以必须在调用它们时规定 
作用域。大家也许认为必须用上述的方法将所有Win32 API (函数、常数和数据类型)都映射成Java 类。但 
幸运的是,根本不必这样做。
A。3。2 。ms。win32 包
Win32 API 的体积相当庞大——包含了数以千计的函数、常数以及数据类型。当然,我们并不想将每个Win32
API 函数都写成对应Java 形式。微软考虑到了这个问题,发行了一个Java 包,可通过 J/Direct 将 Win32
API 映射成 Java 类。?
小说推荐
返回首页返回目录