`
mondayw
  • 浏览: 144231 次
  • 性别: Icon_minigender_2
  • 来自: 广州
社区版块
存档分类
最新评论

[译文] 使用JNA来简化对本地代码的访问[二]

阅读更多

原文:Simplify Native Code Access with JNA

作者:Sanjay Dasgupta

出处:http://today.java.net/article/2009/11/11/simplify-native-code-access-jna

 

 

 

[译文] 使用JNA来简化对本地代码的访问[一]

 

Java语言编写的C结构

 

C函数通常使用结构(struct)类型作为参数,不过由于Java没有结构类型,于是JNA使用类来代替。类与结构密切相关,因此相关的Java代码看起来很直观,而且效果很好。以下的代码是从kernel32.dll的代理接口Kernel32.java中提取出来的,说明了用以支持GetSystemTime()函数的结构SYSTEMTIMEJava类的转换。

 

import com.sun.jna.Library;

import com.sun.jna.Structure;

 

public interface Kernel32 extends Library {

  //  ... (被删除的其他成员) ...

  public static class SYSTEMTIME extends Structure {

    public short wYear;

    public short wMonth;

    public short wDayOfWeek;

    public short wDay;

    public short wHour;

    public short wMinute;

    public short wSecond;

    public short wMilliseconds;

  }

  void GetSystemTime(SYSTEMTIME st);

  //  ... (被删除的其他成员) ...

}

 

 

需要注意的是,代替C的结构类型的Java类必须扩展JNAcom.sun.jna.Structure基类,把这些类内嵌在代理接口里面有助于在单个文件中整齐地安排每一样东西,当结构只是被同一个代理接口中的函数使用的时候这种做法特别有效。不过这些类也可以定义成独立的公共类(在代理接口之外),如果优先选择或者要求这样做的话。JNA网站有更多关于这些方面的资料。

下面显示的代码,即示例代码中的GetSystemTime.java,说明了对结构类型的使用。在这一例子中,被调用的函数使用结构来把信息传递“出来”,不过结构也可以用来把信息传递“进去”(如同在WindowsSetSystemTime()函数中的情况那样)或者“进去和出来”亦可。

 

import libs.Kernel32;

import libs.Kernel32.SYSTEMTIME;

import com.sun.jna.Native;

 

public class GetSystemTime {

 

  public static void main(String[] args) {

    Kernel32 kernel32 = (Kernel32)

        Native.loadLibrary("kernel32", Kernel32.class);

    SYSTEMTIME st = new SYSTEMTIME();

    kernel32.GetSystemTime(st);

    System.out.printf("Year: %d%n", st.wYear);

    System.out.printf("Month: %d%n", st.wMonth);

    System.out.printf("Day: %d%n", st.wDay);

    System.out.printf("Hour: %d%n", st.wHour);

    System.out.printf("Minute: %d%n", st.wMinute);

    System.out.printf("Second: %d%n", st.wSecond);

  }

}

 

 

按照后面“运行示例代码”一节中的指引来编译和运行这一程序。

正确地找出被转换结构的每个成员的类型是很重要的,在这里犯错误通常会导致灾难性的后果,你可以通过修改SYSTEMTIME中的类型来体验一下。JNA有一些其他的技巧可以用来指定某个结构应该是通过引用(缺省情况)还是通过值来传递,以及内嵌在另一结构中的结构应如何存储。JNA网站有许多关于这些方面的指导,后面题为“从JNI转换到JNA”的一节中有几个关于C结构到Java类转换的例子。

在没有还要考虑内存对齐要求的情况下,关于跨语言的结构可移植性的讨论是不会完整的,由于文章的这部分内容主要致力于JNA的基础,因此我们把关于对齐需求的讨论推迟到较后面的一节“从JNI转换到JNA”中。

 

指针和字符串

 

CC++和某些其他语言中使用指针是一件非常自然的事情,但是指针的使用也激增了Java的发明者想要防止的某些错误和编程弊端,因此,虽然Java程序与C++代码极为相似,但是Java没有指针。不过这样或者那样的指针通常都会作为参数在本地函数中使用,因此JNA程序在解决这一限制方面必须要有些创造性。

下面的例子(GetVolumeInformation.java)利用了JavaC继承的语言功能:对数组的引用是指向数组的第一个元素的指针。

 

import libs.Kernel32;

import com.sun.jna.Native;

 

public class GetVolumeInformation {

        

  private static String b2s(byte b[]) {

    // Converts C string to Java String

    int len = 0;

    while (b[len] != 0)

      ++len;

    return new String(b, 0, len);

  }

 

  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) {

      if ((drives & (1 << i)) == 0)

        continue;

      String path = String.format("%c:\\", (char) ((int) 'A' + i));

      byte volName[] = new byte[256], fsName[] = new byte[256];

      int volSerNbr[] = new int[1], maxCompLen[] = new int[1], fileSysFlags[] = new int[1];

      boolean ok = kernel32.GetVolumeInformationA(path, volName,

          256, volSerNbr, maxCompLen, fileSysFlags, fsName, 256);

      if (ok)

        System.out.printf("%s %08X '%s' %s %08X%n", path, volSerNbr[0],

            b2s(volName), b2s(fsName), fileSysFlags[0]);

      else

        System.out.printf("%s (Offline)%n", path);

    }

  }

}

 

 

GetVolumeInformation()的规范说明了它的第四个到第六个参数(前面黑体强调的部分)的类型是LPDWORD,这一类型可理解成“指向整型的指针”,我们使用整型数组作为这些参数的替换,这样就可以绕过Java没有指针这个问题。因此,在代理的方法声明中,这些参数都被定义成int[]类型,然后我们在运行时(参加前面的代码)传入只有一个元素的整型数组,由GetVolumeInformation()返回的值则放在这个填充每个数组的单个整型元素中。

该程序的输出如下所示,在我的计算机上,D:是一个CD-ROM驱动器,在捕获这一输出的时候,该驱动器并没有处于加载的状态,设备G:则是一个USB闪存驱动器。

 

C:\ 609260D7 'My-C-Drive' NTFS 000700FF

D:\ (Offline)

E:\ C8BCF084 'My-E-Drive' NTFS 000700FF

G:\ 634BE81B 'SDG-4GB-DRV' FAT32 00000006

 

 

按照后面“运行示例代码”一节中的指引来编译和运行这一程序。

在前面的代码中有另一件需要注意的事情,那就是字符串传递给本地代码以及从本地代码中传递出来的方式。JavaString不需要特别的处理(查看前面代码中的变量path)就可被传“入”本地代码中,不过传递“出来”给Java的带有null终止符的串则需要谨慎地处理,可以查看一下变量volNamefsName的使用方式,以及前面代码中的方法b2s(byte b[])。最后一点需要注意的是,GetVolumeInformation()是一个宏,其“真正的”名称是GetVolumeInformationA(),可参阅该函数的规范了解所有详情。

Java中处理指针的另一种做法是基于包com.sun.jna.ptr中的类以及类com.sun.jna.Pointer,可以在下面“从JNI转换到JNA”一节中讨论的代码那里找到这些类的使用例子。

 

JNI转换到JNA

 

讨论完基础的功能之后,现在是时候用一些更丰盛的东西来充实一下你的头脑了。本文接下来的部分讨论在转换现有应用(基于JNI)到JNA方面所面临的问题,审视这些转换代码(包含在示例代码中)就会给你提供更加深入的了解,明白JNA如何能够被用来处理“真实的”应用的复杂性。本文所使用的JNI代码来自文章“Tech: Acquire Images with TWAIN and SANE, Part 1”,该文描述了如何使用TWAIN库从扫描仪摄像头以及其他的影像设备中获取图像。

要运行TWAIN代码的话,你最好有一个TWAIN设备(扫描仪、摄像头之类的)连接到你的计算机上,不过如果你的计算机没有安装TWAIN设备的话,你应该下载并安装TWAIN Developer Toolkit,该工具包包含了一个模拟图像源的程序。为了理解该代码,你还需要有可用的TWAIN头文件。

如果要运行TWAIN演示程序的话就执行后面的“运行示例代码”中说明的JTwainDemo.bat文件,为了理解程序的整个流程,按照原来的JNI文章中的Let There Be TWAIN一节开始的指引来做。

下面的图2说明了对来自该JNI文章的示例代码所做的修改。

 

 



 

2:对来自JNI文章的“code.zip”的修改

 

jtwain.cpptwain.h已被删除,因为他们只包含JNI特定的代码,Philos.java被删除是因为它与TWAIN或者JNI无关,JTwain.java被修改成包含TWAIN功能的一个JNA实现而不是初始的JNA代码。包libs是新建的,它包含的三个文件(Kernel32.javaUser32.javaWin32Twain.java)是本文中讨论的代理接口。余下的三个文件则保持不变。可以觉察到包含了前面描述的那些简单例子程序的包democode并未在图2中显示出来。

TWAIN代码到JNA的转换过程照例提供了任何非一般项目都具有的学习经验,不过它也引发了一种罕见的以及隐蔽的错误——结构内存对齐错误——这是使用本地代码的Java工程所特有的。由于内存对齐错误很难被检测到,因此对于许多Java用户来说,它们可能还比较新鲜,以下几节内容提供了处理这些错误的详细指导。

 

[译文] 使用JNA来简化对本地代码的访问[三]

 

  • 大小: 5.3 KB
分享到:
评论

相关推荐

    Java Native Access(JNA):简化Java与本地库交互的桥梁

    与Java Native Interface(JNI)相比,JNA提供了一种更简单、更安全的方法来调用本地方法,无需编写额外的本地代码包装器或使用Java Native Header(JNH)文件。本文将详细介绍JNA的基本概念、工作原理、使用方式...

    Android通过JNA调用C,C++方法

    本篇文章将详细讲解如何在Android中使用JNA来调用C/C++方法。 首先,了解JNA的基本概念。JNA是一种轻量级的Java库,它允许Java程序直接调用动态链接库(DLL)或共享库(SO)中的函数,无需编写JNI代码。相比之下,...

    JNA调用C++动态库,传入二维数组,通过C++返回二维数组,java调用C++完整案例

    本案例将详细介绍如何使用JNA来调用C++动态库,特别是处理二维数组的输入和输出。 首先,我们需要了解JNA的基本原理。JNA通过映射Java接口到本地函数,实现了跨语言调用。它提供了一个简洁的方式来定义C/C++函数...

    如何使用JNA调用本地C/C++动态链接库详细示例代码

    详细演示了如何通过JNA实现以下形式的动态链接库接口: 1:基本数据类型 2:基本数据类型的指针和引用 3:结构体 4:结构体的指针和引用 5:函数指针和回调函数 6:字符串指针 7:输入一个数组 8:输出一个数组并...

    jna java代码生成工具

    1. **工具介绍**:JNAERA(可能是JNA Easier Rapid Access的缩写)是一个基于JNA的代码生成工具,旨在简化与本地库的集成过程。它通过分析C/C++头文件,自动生成对应的Java接口代码。 2. **安装与配置**:文章可能...

    使用JNA替代JNI调用DLL,并解决内存溢出问题

    JNA通过一个小型的JNI库来实现对本地代码的动态调用。其主要特点包括: - 开发者仅需通过Java接口描述目标本地库的功能和结构,从而轻松利用本机平台的功能,而无需为多平台配置和生成JNI代码付出高昂的成本。 - JNA...

    jna.rar_jna_jna.jar_jna文件

    总的来说,JNA为Java开发者提供了一种强大而灵活的方式来与本地代码交互,使得Java应用能够充分利用操作系统提供的各种功能,而无需编写或管理本地代码。无论你是想进行系统级编程、调用已有的C/C++库还是操作硬件...

    JNA Java源代码

    Java Native Access(JNA)是Java平台上的一个开源库,它允许Java代码直接与本地操作系统API进行交互,而无需编写JNI(Java Native Interface)代码。JNA通过动态加载库并映射函数,使得Java程序员可以方便地调用C、...

    jna-master测试代码源代码.zip

    8. **性能**:尽管JNA相比JNI可能稍慢,但其易用性和简洁性使得它成为许多项目首选的原生访问方式,特别是在需要快速原型开发或者对性能要求不那么高的情况下。 9. **错误处理**:JNA通过抛出`LastErrorException`...

    JNA的使用

    总之,JNA是Java开发者访问本地资源的重要工具,它简化了Java与本地代码的交互过程,使得Java应用能够充分利用操作系统提供的功能。然而,使用时需要注意性能和类型兼容性的问题,合理选择何时使用JNA,何时编写JNI...

    jna-3.3.0 & jna-3.3.0-platform

    JNA通过映射Java方法到本地函数,实现了Java与操作系统底层功能的交互,极大地简化了在Java应用中使用系统API或DLL库的过程。在这个场景中,我们有两个文件:`jna-3.3.0.jar`和`jna-3.3.0-platform.jar`,它们分别是...

    使用JNA的jar包

    JNA提供了更简洁、更易用的方式来实现Java与C/C++等本地代码之间的交互,极大地简化了跨语言通信的过程。 在Java中,通常有两种方式来调用本地库:JNI(Java Native Interface)和JNA。JNI虽然功能强大,但它的学习...

    jna 现成的java调用jna项目 包括jna.jar

    Java Native Access (JNA) 是一个Java库,它允许Java代码与本地库进行交互,无需编写C语言的JNI(Java Native Interface)代码。这个技术极大地简化了Java应用程序调用操作系统功能或者使用非Java编写的库的过程。在...

    JNA 3.3.0 API

    它提供已描述好本地函数类型的一组工具接口来简化本地访问. JNA与JNI比较, 它不需要生成DLL, JNA基于JNI技术, 简化了JAVA本地访问的过程, 但性能不如JNI. 从之前发布其他chm文件下载用户的反映看,有不少朋友反映...

    jna整合包-jna4.5.0+jna4.0.0.rar

    总的来说,JNA作为一个强大的库,简化了Java与本地库交互的过程,使得开发者可以在Java环境中充分利用系统资源,实现更丰富的功能。在使用过程中,需要注意版本间的差异,以及正确配置和管理本地库的依赖。同时,...

    JNA的相关jarbao

    然而,尽管JNA简化了Java与本地代码的交互,但它并不总是最快的解决方案。对于性能敏感的应用,JNI可能仍然是更好的选择,因为JNI可以直接生成机器码,而JNA则需要额外的内存管理和类型映射开销。此外,JNA的自动...

    com.sun.jna 3.0.9

    9. **JNA的局限性**:虽然JNA简化了跨语言调用,但它并不适合所有情况,特别是需要高性能或复杂的本地代码交互时,JNI可能仍然是更好的选择。 10. **使用JNA的步骤**:创建本地函数接口,映射Java类型,加载动态...

    jna-3.0.9.jar和examples.jar

    总结起来,"jna-3.0.9.jar和examples.jar"是Java开发中用于本地库交互的重要工具,它们可以帮助开发者轻松地访问和控制本地系统资源,例如与硬件设备进行通信,或者使用本地库提供的复杂功能。结合"JFrame",可以...

    使用JNA获取窗口句柄,并发送消息

    JNA提供了一种无需编写C代码或使用JNI(Java Native Interface)即可调用操作系统API的方法。本话题将详细介绍如何使用JNA获取窗口句柄并发送消息,以及在CS(Client-Server)架构中实现SSO(Single Sign-On)。 ...

    JNA 使用方法

    3. 声明本地库:使用 JNA 的 @Library 注解来声明本地库,例如 public interface CLibrary extends Library { ... } 4. 调用本地方法:使用 JNA 的 Native 类来调用本地方法,例如 CLibrary.INSTANCE.printf("Hello,...

Global site tag (gtag.js) - Google Analytics