友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!阅读过程发现任何错误请告诉我们,谢谢!! 报告错误
一世书城 返回本书目录 我的书架 我的书签 TXT全本下载 进入书吧 加入书签

Java编程思想第4版[中文版](PDF格式)-第210章

按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!



}  

  

下面这个例子展示了如何同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 类。这个包的名字叫作 。ms。win32。安装Java SDK 2。0 时,若在安装选项中进行了相应 

的设置,这个包就会安装到我们的类路径中。这个包由大量Java 类构成,它们完整再现了 Win32 API 的常 

数、数据类型以及函数。包容能力最大的三个类是 User32。class,Kernel。class 以及Gdi32。class。它们包 

含的是Win32 API 的核心内容。为使用它们,只需在自己的 Java 代码里导入即可。前面的ShowMsgBox 示例 

可用。ms。win32 改写成下面这个样子(这里也考虑到了用更恰当的方式使用UnsatisfiedLinkError):  

  

import 。ms。win32。*;  



                                                                                             657 


…………………………………………………………Page 659……………………………………………………………

  

public class UseWin32Package {  

  public static void main(String args'') {  

    try {  

      User32。MessageBeep(  

        winm。MB_ICONEXCLAMATION);  

      User32。MessageBox(0;  

        〃Created by the MessageBox() Win32 func〃;  

        〃Thinking in Java〃;  

        winm。MB_OKCANCEL |  

        winm。MB_ICONEXCLAMATION);  

    } catch(UnsatisfiedLinkError e) {  

      System。out。println(〃Can’t link Win32 API〃);   

      System。out。println(e);  

    }  

  }  

}  

  

Java 包是在第一行导入的。现在,可在不进行其他声明的前提下调用MessageBeep()和 MessageBox()函数。 

在MessageBeep()里,我们可看到包导入时也声明了 Win32 常数。这些常数是在大量Java 接口里定义的,全 

部命名为winx (x 代表欲使用之常数的首字母)。  

写作本书时,。ms。win32 包的开发仍未正式完成,但已可堪使用。  



A。3。3  汇集  



 “汇集”(Marshaling)是指将一个函数自变量从它原始的二进制形式转换成与语言无关的某种形式,再将 

这种通用形式转换成适合调用函数采用的二进制格式。在前面的例子中,我们调用了MessageBox()函数,并 

向它传递了两个字串。MessageBox()是个C 函数,而且 Java 字串的二进制布局与C 字串并不相同。但尽管如 

此,自变量仍获得了正确的传递。这是由于在调用 C 代码前,J/Direct 已帮我们考虑到了将Java 字串转换 

成C 字串的问题。这种情况适合所有标准的Java 类型。下面这张表格总结了简单数据类型的默认对应关系:  

  

Java C  

  

byte BYTE 或CHAR  

short SHORT或 WORD  

int INT,UINT ,LONG,ULONG 或 DWORD  

char TCHAR  

long __int64  

float Float  

double Double  

boolean BOOL  

String LPCTSTR (只允许在OLE 模式中作为返回值)  

byte'' BYTE *  

short'' WORD *  

char'' TCHAR *  

int'' DWORD *  

  

这个列表还可继续下去,但已很能说明问题了。大多数情况下,我们不必关心与简单数据类型之间的转换问 

题。但一旦必须传递用户自定义类型的自变量,情况就立即变得不同了。例如,可能需要传递一个结构化 

的、用户自定义的数据类型,或者需要把一个指针传给原始内存区域。在这些情况下,有一些特殊的编译引 

导命令标记一个Java 类,使其能作为一个指针传给结构(@dll。struct 引导命令)。欲知使用这些关键字的 

细节,请参考产品文档。  



                                                                                           658 


…………………………………………………………Page 660……………………………………………………………

A。3。4  编写回调函数  



有些Win32 API 函数要求将一个函数指针作为自己的参数使用。Windows API 函数随后就可以调用自变量函 

数(通常是在以后发生特定的事件时)。这一技术就叫作“回调函数”。回调函数的例子包括窗口进程以及 

我们在打印过程中设置的回调(为后台打印程序提供回调函数的地址,使其能更新状态,并在必要的时候中 

止打印)。  

另一个例子是API 函数EnumWindows() ,它能枚举目前系统内所有顶级窗口。EnumWindows()要求获取一个函 

数指针作为自己的参数,然后搜索由Windows 内部维护的一个列表。对于列表内的每个窗口,它都会调用回 

调函数,将窗口句柄作为一个自变量传给回调。  

为了在Java 里达到同样的目的,必须使用 。ms。dll 包里的 Callback 类。我们从 Callback 里继承,并取 

消callback()。这个方法只能接近int 参数,并会返回 int或void。方法签名和具体的实施取决于使用这个 

回调的Windows API 函数。  

现在,我们要进行的全部工作就是创建这个Callback 衍生类的一个实例,并将其作为函数指针传递给API 函 

数。随后,J/Direct 会帮助我们自动完成剩余的工作。  

下面这个例子调用了Win32 API 函数EnumWindows() ;EnumWindowsProc 类里的 callback()方法会获取每个 

顶级窗口的句柄,获取标题文字,并将其打印到控制台窗口。  

  

import 。ms。dll。*;  

import 。ms。win32。*;  

  

class EnumWindowsProc extends Callback {  

  public boolean callback(int hwnd; int lparam) {  

    StringBuffer text = new StringBuffer(50);  

    User32。GetWindowText(  

      hwnd; text; text。capacity()+1);  

    if(text。length() != 0)  

      System。out。println(text);  

    return true;  // to continue enumeration。  

  }  

}  

  

public class ShowCallback {  

  public static void main(String args'')  

  throws InterruptedException {  

    boolean ok = User32。EnumWindows(  

      new EnumWindowsProc(); 0);  

    if(!ok)  

      System。err。println(〃EnumWindows failed。〃);  

    Thread。currentThread()。sleep(3000);  

  }  

}  

  

对 sleep()的调用允许窗口进程在main()退出前完成。  



A。3。5  其他 J/Direct 特性  



通过@dll。import 引导命令内的修改符(标记),还可用到 J/Direct 的另两项特性。第一项是对OLE 函数的 

简化访问,第二项是选择API 函数的ANSI 及 Unicode 版本。  

根据约定,所有OLE 函数都会返回类型为HRESULT 的一个值,它是由  定义的一个结构化整数值。若在 

 那一级编写程序,并希望从一个OLE 函数里返回某些不同的东西,就必须将一个特殊的指针传递给它— 

—该指针指向函数即将在其中填充数据的那个内存区域。但在 Java 中,我们没有指针可用;此外,这种方法 

并不简练。利用J/Direct,我们可在@dll。import 引导命令里使用ole 修改符,从而方便地调用OLE 函数。 

标记为 ole 函数的一个固有方法会从Java 形式的方法签名(通过它决定返回类型)自动转换成  形式的函 



                                                                                       659 


…………………………………………………………Page 661……………………………………………………………

数。  

第二项特性是选择ANSI 或者 Unicode 字串控制方法。对字串进行控制的大多数 Win32 API 函数都提供了两个 

版本。例如,假设我们观察由 USER32。DLL 导出的符号,那么不会找到一个MessageBox()函数,相反会看到 

MessageBoxA()和MessageBoxW() 函数——分别是该函数的ANSI 和 Unicode 版本。如果在@dll。import 引导命 

令里不规定想调用哪个版本,JV
返回目录 上一页 下一页 回到顶部 0 0
未阅读完?加入书签已便下次继续阅读!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!