[译文] 使用JNA来简化对本地代码的访问[三]
2012-07-09 23:24
106 查看
原文: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); // 终止:内存对齐问题演示 |
ProtocolMajor: 01 ProtocolMinor: 09 SupportedGroups: 694d0000 Manufacturer: crosoft Tw |
现在让我们来看看下面展示的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 ... (基于清晰的目的而删除的其他行) ... |
从上面的转储内容看来,很明显本地代码返回的信息是正确的(应该可以想到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个字节的最小分界线 |
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; . . . } |
运行示例代码
按照如下步骤来运行本文中描述的示例代码:
下载包含了示例代码的压缩文件,并把它解压到某个目录(例如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(在多年使用了多种其他的语言之后),他是一个独立顾问,现在住在印度的加尔各答。
相关文章推荐
- [译文] 使用JNA来简化对本地代码的访问[一]
- [译文] 使用JNA来简化对本地代码的访问[二]
- java使用jna访问本地库
- Windows 下 使用 Fiddler + nginx 本地 debug 手机APP所访问的后台代码
- 使用Lombok简化你的代码
- TiXmlHandle的使用-简化tinyxml的代码
- Yii2使用驼峰命名的形式访问控制器的示例代码
- 使用ngrok本地开发微信,代码无需上传到服务器
- 『Python CoolBook』使用ctypes访问C代码_上_用法讲解
- GitHub使用(二)-- 从代码库下载代码到本地
- 借助apktool.jar工具,使用python代码简化批量反编译apk安装包的简单实现
- Git使用手册/Git教程:git fetch 将远程仓库的分支及分支最新版本代码拉取到本地
- 使用本地git管理xcode代码:
- java REST入门:使用 JAX-RS 简化 REST 应用开发(转自代码商人)
- 使用javascript来访问本地文件夹
- Eclipse恢复已删除的文件和代码、svn使用了还原,但本地的没有提交找回没提交代码的方法
- JNI的替代者—使用JNA访问Java外部功能接口
- 使用git当修改本地代码后无法同步代码的处理
- 在ubuntu下使用python代码访问mysql
- 使用python的列表解析以及函数式计算来简化代码