施用JNA来简单化对当地代码的访问[2]

本文介绍如何使用Java Native Access (JNA) 库实现Java与C语言之间的互操作,包括结构体传递、指针和字符串处理等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

用Java语言编撰的C构造


C函数正常应用构造(struct)门类作为参数,不过因为Java没构造部类,于是乎JNA应用种来替代。种与构造密切相关,因而有关的Java代码看上去很直观,并且成效良好。以次的代码是从kernel32.dll的署理接口Kernel32.java中提取出来的,说了然用来支持GetSystemTime()函数的构造SYSTEMTIME到Java种的变换。



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种务须扩张JNA的com.sun.jna.Structure基类,把这些类内嵌在署理接口里头有助于在单个资料中齐整地安顿每同样货色,应构造只是被同一个署理接口中的函数施用的时分这种做法不一般有效。不过这些种也可以定义成独力的公共种(在署理接口以外),如其优先选择或许要求这么干的话。JNA网站有更多对于这些方面的文件。

下部展示的代码,即示范代码中的GetSystemTime.java,说了然对构造部类的施用。在这一例证中,被调用的函数运用构造来把信息传送“出来”,不过构造也可以用于把信息传送“进来”(有如在Windows的SetSystemTime()函数中的景况那样)可能“进来和出来”亦可。



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”中。



指针和字符串


在C、C++和某些其余语言中运用指针是一件十分天然的事儿,但是指针的应用也猛增了Java的发明家想要防止的某些差错和编程弊病,因而,尽管Java程序与C++代码颇为相像,但是Java没指针。不过这么也许那样的指针正常都市作为参数在本土函数中运用,因而JNA程序在解决这一限制方面务必要有点创造性。

底下的事例(GetVolumeInformation.java)利用了Java从C承袭的语言效能:对数组的引述是指向数组的第一个元素的指针。



import libs.Kernel32;

import com.sun.jna.Native;



public class GetVolumeInformation {



private static String b二s(byte b[]) {

// Converts C string to Java String

int len = 零;

while (b[len] != 零)

++len;

return new String(b, 零, len);

}



public static void main(String[] args) {

Kernel32 kernel32 = (Kernel32) Native.loadLibrary(

"kernel32", Kernel32.class);

int drives = kernel32.GetLogicalDrives();

for (int i = 零; i < 32; ++i) {

if ((drives & (一 << i)) == 零)

continue;

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

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

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

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[零],

b二s(volName), b二s(fsName), fileSysFlags[零]);

else

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

}

}

}






GetVolumeInformation()的轨范说了然它的第四个到第六个参数(前边黑体夸大的一部分)的门类是LPDWORD,这一门类可理解成“指向整型的指针”,我们施用整型数组作为这些参数的轮换,这么就可以绕过Java没指针这个问题。因而,在署理的步骤宣言中,这些参数都被定义成int[]部类,其后我们在运行时(参与前头的代码)传到惟独一个元素的整型数组,由GetVolumeInformation()回来的值则放在这个填充每个数组的单个整型元素中。

该程序的输出如下所示,在我的电脑上,D:是一个CD-ROM驱动器,在擒获这一输出的时分,该驱动器并没有居于加载的状态,装置G:则是一个USB闪存驱动器。



C:/ 609260D七 'My-C-Drive' NTFS 000700FF

D:/ (Offline)

E:/ C八BCF084 'My-E-Drive' NTFS 000700FF

G:/ 634BE81B 'SDG-四GB-DRV' FAT32 00000006






依照后边“运作示范代码”一节中的向导来编译和运作这一程序。

在前面的代码中有另一件亟需注意的事儿,那就是字符串传接给当地代码以及从当地代码中传送出来的模式。Java的String不需要不一般的处置(查看前头代码中的变量path)就可被传“进”当地代码中,不过传送“出来”给Java的含有null停止符的串则亟需审慎地处置,可以查看一下变量volName和fsName的应用形式,以及前边代码中的步骤b二s(byte b[])。最后1点急需注意的是,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 一”,该文描述了何以运用TWAIN库从扫描仪摄像头以及其余的影像装置中获取图像。

要运作TWAIN代码的话,你最好有一个TWAIN装置(扫描仪、摄像头等等的)联接到你的电脑上,不过如若你的电脑没装配TWAIN装置的话,你应当下载并装配TWAIN Developer Toolkit,该工具包包孕了一个模拟图像流的程序。为了懂得该代码,你还需要有可用的TWAIN头资料。

如若要运作TWAIN演练程序的话就实施后头的“运作示范代码”中说明的JTwainDemo.bat资料,为了懂得程序的整个流程,依照原来的JNI稿件中的Let There Be TWAIN一节开始的向导来干。

下部的图二说了然对来源于该JNI稿件的示范代码所做的批改。








图二:对来源于JNI稿件的“code.zip”的批改



jtwain.cpp和twain.h已被剔除,由于他们只包孕JNI特定的代码,Philos.java被剔除是由于它与TWAIN也许JNI无干,JTwain.java被批改成包孕TWAIN效能的一个JNA兑现而不是初步的JNA代码。包libs是新建的,它包孕的三个资料(Kernel32.java、User32.java和Win32Twain.java)是正文中议论的署理接口。剩余的三个资料则护持不变。可以觉察到包孕了前头描述的那些简略事例程序的包democode尚无在图2中显示出来。

TWAIN代码到JNA的变换历程照旧提供了任何非正常项目都具有的学习心得,不过它也挑动了一种少见的以及掩蔽的错处——构造内存储器对齐错处——这是运用本土代码的Java工程所特有的。因为内存储器对齐差错很难被检测到,因而至于好多Java用户来说,它们也许还比较新鲜,以次几节内容提供了处置这些错处的详细指点。


未完待续

本文来源:
我的异常网
Java Exception
Dotnet Exception
Oracle Exception

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值