原文:Simplify Native Code Access with JNA
作者:Sanjay Dasgupta
出处:http://today.java.net/article/2009/11/11/simplify-native-code-access-jna
[译文] 使用JNA来简化对本地代码的访问[一]
[译文] 使用JNA来简化对本地代码的访问[二]
检测结构的内存对齐问题
这里的情况是问题存在于细节中,故不能简单地、非侵入性地做出结论说,某个特别的错误是因为内存对齐不匹配造成的,而是要通过下面这些必定乏味的讨论来找出一种方法。
相关的代码(如下所示)在JTwain.java中,可以通过搜索“Memory Alignment Problem Demo”来找到位置。该代码创建了一个TW_IDENTITY(一个Java的结构代替)实例,并把它传递给TWAIN运行时,TWAIN接着与用户交互以选择一个源设备,TW_IDENTITY实例在被从Java传递给TWAIN时是未初始化的,但在返回时填充了关于选中设备的信息,代码最后的print()和dump()显示结构的各个部分以便在检测问题时有所帮助。
//起始: 内存对齐问题演示
TW_IDENTITY srcID = new TW_IDENTITY(Structure.ALIGN_DEFAULT);
stat = SelectSource(g_AppID, srcID);
if (stat != TWRC_SUCCESS) {
//... (基于清晰的目的而删除的一些行) ...
}
System.out.printf("ProtocolMajor: %02x%n", srcID.ProtocolMajor);
System.out.printf("ProtocolMinor: %02x%n", srcID.ProtocolMinor);
System.out.printf("SupportedGroups: %04x%n", srcID.SupportedGroups);
System.out.printf("Manufacturer: %s%n", new String(srcID.Manufacturer, 0, 34));
dump(srcID);
// 终止:内存对齐问题演示
|
下面显示的printf()语句的输出明显示意了有内存对齐问题存在:
ProtocolMajor: 01
ProtocolMinor: 09
SupportedGroups: 694d0000
Manufacturer: crosoft Tw
|
头两个值:ProtocolMajor和ProtocolMinor是正确的,但是接下来的两个则明显是有问题的,在之前的TWAIN调用中,Java代码议定SupportedGroups的值为0x0003,因此应该返回同一个值才对,还有Manufacturer的值看上去明显像似“Microsoft”的头两个字母被砍掉了。
现在让我们来看看下面展示的dump()的输出,“dump”显示结构在从本地代码接收到时的内容,还没有被JNA分离成单个的成员值。
000: 11 04 00 00 01 00 00 00 0d 00 01 00 32 36 20 4a
000: . . . . . . . . . . . . 2 6 J
016: 75 6e 65 20 32 30 30 30 00 00 00 00 00 00 00 00
016: u n e 2 0 0 0 . . . . . . . .
032: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00
032: . . . . . . . . . . . . . . . .
048: 09 00 03 00 00 00 4d 69 63 72 6f 73 6f 66 74 00
048: . . . . . . M i c r o s o f t .
064: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
064: . . . . . . . . . . . . . . . .
080: 00 00 00 00 00 00 00 00 54 77 61 69 6e 20 44 61
080: . . . . . . . . T w a i n D a
... (基于清晰的目的而删除的其他行) ...
|
dump的每一行显示16个字节的“原始”内存的值,每行开始时的数值(在冒号之前)是该行从结构开始的位置算起的偏移值,每组16个字节打印两次——首先是16进制的整数,然后是ASCII字符。颜色(手工配上的)用以分隔相邻的结构成员,有下划线的部分则是内嵌在结构TW_IDENTITY(参见Win32Twain.java)中的结构TW_VERSION,每个成员在结构的内存空间中的位置和所占有的空间取决于每个成员被声明的顺序和大小。
从上面的转储内容看来,很明显本地代码返回的信息是正确的(应该可以想到PC的Inter处理器是小端字节序的(little-endian)),具体来说,那就是在转储内存中的SupportedGroups和Manufacturer的值也都是正确的。
ProtocolMajor(从偏移量46开始的涂成紫色的2个字节)=0x01
ProtocolMinor(从偏移量48开始的涂成青色的两个字节)=0x09
SupportedGroups(从偏移量50开始的涂成紫色的4个字节)=0x0003
Manufacturer(从偏移量54开始的涂成青色的34个字节)=“Microsoft”(使用值为0的字节填充至34个字节)
比较printf()语句打印的值和转储内存中的值就可以看出来,JNA所理解的结构成员的位置从SupportedGroups开始不可思议地后移了两个字节,这是内存对齐问题的典型症状。
对齐错误的出现是因为本地代码把结构成员无任何插入间隔地串在一起,然而JNA代码却期待在倍数于成员长度的内存偏移量位置上找到它们。因此,本地代码把SupportedGroups放在偏移量为50的地方,但是JNA却在偏移量为52(SupportedGroupds的长度4的倍数)的地方开始查找。紧随在SupportedGroups之后的结构成员也被往后推了两个字节,导致了前面显示的Manufacturer值的变化,你现在应该也能够解释“Tw”如何会悄然出现在Manufacturer值的尾部。
最后简短地说一点关于指针的另一个方面的题外话:dump()的代码展示了使用Structure.getPointer()来获取指向结构开始之处的指针的做法,可以把结构当成是字节数组(C程序员的void*),使用由getPointer()返回的com.sun.jna.Pointer对象来访问它。
重现结构对齐错误
文件JTwain.java实际上包含了有内存对齐错误的代码,因此如果希望的话,读者可以就此问题做进一步的研究,不过TWAIN演示程序仍能够正常工作,因为它没有用到结构中的值。
若要重现内存对齐错误的话,可按照后面“运行示例代码”一节中的描述来编译程序,然后执行JTwainDemo.bat。你应该会看到下面图3中的标题为“JTwain Demo”的窗口,在菜单栏中选择“File”->“Select Source…”,如图中所示,一个列出了已安装的TWAIN设备的题为“Select Source”的窗口会弹出来,选择任一个TWAIN设备,然后点击“Select”按钮,这会执行有对齐错误的代码,并会在命令窗口中显示结构TW-VERSION的内容。
图3:运行JTwain演示程序
需要注意的是,你看到的TW_VERSION结构的内容可能会与前面例子展示的值有所不同(除非你已经安装了同样的TWAIN设备),不过你应该能够看到是同样类型的内存对齐问题的迹象。
如果标题为“Select Source”的弹出窗口中没有显示任何的TWAIN设备的话,那么你应该下载并安装TWAIN developer tookit,该工具包模拟一个图像源(前面图3中的“Select Source”窗口中的第一项),返回一个TWAIN标志的图像。
防止结构对齐错误
一些本地库有着不同的内存对齐风格(因为编译器和编译器选项存在差异的缘故),因此,既然JNA通常在不选择重新编译本地代码的情况下使用,故它可以方便地设置使用的对齐策略。
可以通过调用Structure.setAlignType(int alignType)来设置扩展了Structure的Java类的成员的对齐策略,对齐方式有四种选项,如下表中的描述。
对齐规范
|
JNA描述
|
ALIGN_DEFAULT
|
使用平台缺省的对齐
|
ALIGN_GNUC
|
对32位的x86 linux/gcc有效,对齐字段大小,最大为4个字节
|
ALIGN_MSVC
|
对w32/msvc有效,根据字段大小来对齐
|
ALIGN_NONE
|
不对齐,所有字段都用1个字节的最小分界线
|
前面展示的dump()输出表明,TWAIN的本地代码使用了没有特别对齐的策略(前面表中的ALIGN_NONE),不过既然这也不是JNA的缺省设定,那么所有代替C结构的Java类都有一个缺省的构造方法,该方法把对齐方式设置为ALIGN_NONE(参见Win32Twain.java)。下面的代码是结构TW_INDENTITY的带有缺省构造方法的Java类的简略形式。
public class TW_IDENTITY extends Structure {
public TW_IDENTITY() {
setAlignType(Structure.ALIGN_NONE);
}
public int Id;
public TW_VERSION Version = new TW_VERSION();
public short ProtocolMajor;
public short ProtocolMinor;
. . .
}
|
一般来说是无法知道任何具体的本地库所使用的对齐策略的,因此,如果DLL的文档没有说明这部分信息的话,则就需要做一些实验来确定要使用的正确的对齐设置。
运行示例代码
按照如下步骤来运行本文中描述的示例代码:
下载包含了示例代码的压缩文件,并把它解压到某个目录(例如samples)中。
打开一个命令窗口,然后使用“CD”命令进入samples\code目录。
执行批处理文件build.bat,编译所有的代码(并且只要运行一次就可以了),类文件被放置在名为samples\bin的目录下。
若要运行某个程序的话,就执行有着相同名字(例如LockWorkStation.bat,、BeepMorse.bat、GetLogicalDrives.bat、GetSystemTime.bat、GetVolumeInformation.bat或者 JTwainDemo.bat)的批处理文件。
示例的压缩文件中包含了jna.jar,所以不需要再下载其他任何东西,前面列出的批处理文件也已经指定了类路径,所以不需要做任何修改就可以编译和运行示例代码。
资源
本文的示例代码
JNA项目网站
Windows API参考
Windows数据类型
不再需要指针
维基百科指针页面
维基百科“Windows编程/句柄和数据类型”
C字符串
Java技术:使用TWAIN和SANE来获取图像,第1部分
TWAIN开发者工具包
TWAIN头文件
Sanjay Dasgupta自1996年以来一直在使用Java(在多年使用了多种其他的语言之后),他是一个独立顾问,现在住在印度的加尔各答。
- 大小: 11.8 KB
分享到:
相关推荐
与Java Native Interface(JNI)相比,JNA提供了一种更简单、更安全的方法来调用本地方法,无需编写额外的本地代码包装器或使用Java Native Header(JNH)文件。本文将详细介绍JNA的基本概念、工作原理、使用方式...
本篇文章将详细讲解如何在Android中使用JNA来调用C/C++方法。 首先,了解JNA的基本概念。JNA是一种轻量级的Java库,它允许Java程序直接调用动态链接库(DLL)或共享库(SO)中的函数,无需编写JNI代码。相比之下,...
详细演示了如何通过JNA实现以下形式的动态链接库接口: 1:基本数据类型 2:基本数据类型的指针和引用 3:结构体 4:结构体的指针和引用 5:函数指针和回调函数 6:字符串指针 7:输入一个数组 8:输出一个数组并...
JNA通过一个小型的JNI库来实现对本地代码的动态调用。其主要特点包括: - 开发者仅需通过Java接口描述目标本地库的功能和结构,从而轻松利用本机平台的功能,而无需为多平台配置和生成JNI代码付出高昂的成本。 - JNA...
1. **工具介绍**:JNAERA(可能是JNA Easier Rapid Access的缩写)是一个基于JNA的代码生成工具,旨在简化与本地库的集成过程。它通过分析C/C++头文件,自动生成对应的Java接口代码。 2. **安装与配置**:文章可能...
Java Native Access(JNA)是Java平台上的一个开源库,它允许Java代码直接与本地操作系统API进行交互,而无需编写JNI(Java Native Interface)代码。JNA通过动态加载库并映射函数,使得Java程序员可以方便地调用C、...
Java 使用 JNA(Java Native Access)调用C/C++编写的第三方动态库(DLL文件)是一种常见的技术,它允许Java程序直接与本地操作系统接口交互,而无需编写JNI(Java Native Interface)代码。JNA 提供了一种相对简洁...
总的来说,JNA为Java开发者提供了一种强大而灵活的方式来与本地代码交互,使得Java应用能够充分利用操作系统提供的各种功能,而无需编写或管理本地代码。无论你是想进行系统级编程、调用已有的C/C++库还是操作硬件...
8. **性能**:尽管JNA相比JNI可能稍慢,但其易用性和简洁性使得它成为许多项目首选的原生访问方式,特别是在需要快速原型开发或者对性能要求不那么高的情况下。 9. **错误处理**:JNA通过抛出`LastErrorException`...
总之,JNA是Java开发者访问本地资源的重要工具,它简化了Java与本地代码的交互过程,使得Java应用能够充分利用操作系统提供的功能。然而,使用时需要注意性能和类型兼容性的问题,合理选择何时使用JNA,何时编写JNI...
JNA通过映射Java方法到本地函数,实现了Java与操作系统底层功能的交互,极大地简化了在Java应用中使用系统API或DLL库的过程。在这个场景中,我们有两个文件:`jna-3.3.0.jar`和`jna-3.3.0-platform.jar`,它们分别是...
JNA提供了更简洁、更易用的方式来实现Java与C/C++等本地代码之间的交互,极大地简化了跨语言通信的过程。 在Java中,通常有两种方式来调用本地库:JNI(Java Native Interface)和JNA。JNI虽然功能强大,但它的学习...
Java Native Access (JNA) 是一个Java库,它允许Java代码与本地库进行交互,无需编写C语言的JNI(Java Native Interface)代码。这个技术极大地简化了Java应用程序调用操作系统功能或者使用非Java编写的库的过程。在...
它提供已描述好本地函数类型的一组工具接口来简化本地访问. JNA与JNI比较, 它不需要生成DLL, JNA基于JNI技术, 简化了JAVA本地访问的过程, 但性能不如JNI. 从之前发布其他chm文件下载用户的反映看,有不少朋友反映...
总的来说,JNA作为一个强大的库,简化了Java与本地库交互的过程,使得开发者可以在Java环境中充分利用系统资源,实现更丰富的功能。在使用过程中,需要注意版本间的差异,以及正确配置和管理本地库的依赖。同时,...
然而,尽管JNA简化了Java与本地代码的交互,但它并不总是最快的解决方案。对于性能敏感的应用,JNI可能仍然是更好的选择,因为JNI可以直接生成机器码,而JNA则需要额外的内存管理和类型映射开销。此外,JNA的自动...
9. **JNA的局限性**:虽然JNA简化了跨语言调用,但它并不适合所有情况,特别是需要高性能或复杂的本地代码交互时,JNI可能仍然是更好的选择。 10. **使用JNA的步骤**:创建本地函数接口,映射Java类型,加载动态...
总结起来,"jna-3.0.9.jar和examples.jar"是Java开发中用于本地库交互的重要工具,它们可以帮助开发者轻松地访问和控制本地系统资源,例如与硬件设备进行通信,或者使用本地库提供的复杂功能。结合"JFrame",可以...
3. **第三方库集成**:如果有一个本地库没有Java版本,但提供了C或C++的API,JNA可以帮助你轻松地在Java中使用这些库。 4. **性能优化**:对于性能敏感的代码,使用JNA调用本地库可能比纯Java实现更快。 **使用JNA...
JNA提供了一种无需编写C代码或使用JNI(Java Native Interface)即可调用操作系统API的方法。本话题将详细介绍如何使用JNA获取窗口句柄并发送消息,以及在CS(Client-Server)架构中实现SSO(Single Sign-On)。 ...