Jna详细教程
JNA(JavaNativeAccess)框架是一个开源的Java框架,是SUN公司主导开发的,建立在经典的JNI的基础之上的一个框架。JNA项目地址:https://jna.dev.java.net/JNA使Java调用原生函数就像.NET上的P/Invoke一样方便、快捷。JNA的功能和P/Invoke类似,但编写方法与P/Invoke截然不同。JNA没有使用Annotation,而是通过编写一般的Java代码来实现。P/Invoke是.NET平台的机制。而JNA是Java平台上的一个开源类库,和其他类库没有什么区别。只需要在classpath下加入jna.jar包,就可以使调用原生函数的过程JNA提供了一个动态的C语言编写的转发器,可以自动实现Java和C的数据类型映射你不再需要编写那个烦人的C动态链接库。当然,这也意味着,使用JNA技术比使用N技术调用动态链接库会有些微的性能损失。可能速度会降低几倍。但对于绝大部分项目来说,影响不大JNA调用原生函数让我们先看一个JNA调用原生函数的例子。例子1使用JNA调用原生函数假设我们有一个动态链接戽,发布了这样一个C函数:VOid saylwchar t* pvalue)std: wcout imbue(std: locale " chs )std:wcou<"原生函数说:"< pvalue(基本类型的数组)pointerarray邻接内存结构体成员)JNA支持常见的数据类型的映射Java类型c类型原生表现Stringchar+10结束的数组( native encoding or ]na. cncoding)WStringwchar_ti*|o结束的数组 (unicode)String1cha*10结束的数组的数组WString] wchar_**小0结束的宽字数组的数组指向结构体的指针(参数或返回值)(或者明确指定是结构体指structStructure针)struct结构体结构体的成员)或者明确指定是结构体)union等同丁结构体tructure[l[结构体的数组,邻接内存callback k>c0Jaxa数指针或原生函数指针NativeMapped varies依赖丁定义NativeLong|ong半台依赖(32或64位整数PointerType pointer‖和 Pointer相同跨平台、跨语言调用原则:尽量使用基本、简单的数据类型尽量少跨平台、跨语言传递数据!如果有复杂的数据炎型需要在Java和原生函数中传递,那么我们就必须在Java中模拟大量复杂的原生类型。这将人大增加实现的难度,甚至无法实现如果在Java和原生函数间存在大量的数据传递,那么一方面,性能会有很大的损失。更为重要的是,Java调用原生函数时,会把数据固定在内存中,这样原生函数才可以访问这些Java数据。这些数据,丿VM的GC不能管理,会造成内存碎片。如果在你需要调用的动态链接库中,有复杂的数据类型和庞大的跨平台数据传递。那么你应该另外写一些原生函数,把需要传递的数据类型简化,把需要传递的据量简化。JNA模拟结构体在原生代码中,结构体是经常使用的复杂数据类型。这里我们硏究一下怎样使用JNA模拟结构体例2使用NA调用使用 Struct的C函数假设我们现在有这样一个C语言结构体struct User Structongwchar t* nameInt age;使用上述结构体的函数define mylibapi extern"Cdeclspec( dllexportMYLIBAPI void sayUser(UserStruct* pUserStruct)对应的Java程序中,在例1的接口中添加下刎代码:public static class Userstruct extends structurePublic Nativelong idipublic WString name;public int agepublic static class ByReference extends Userstructmplements structure ByReference[ jublic static class ByValue extends Userstruct implementsStructure. ByValuepublic void sayUser(Userstruct ByReference struct)iava中的调用代码:Userstruct userstruct-new Userstruct (iuserstruct. id=new NativeLong (100userstruct. age=30;userstruct.r:me= new String("奥巴马");TestDIIl. INSTANCE. sayUser(userstruct)iStructure说明现在,我们就在Java中实现了对C语言的结构体的模拟。这里,我们继承了 structure类,用这个类来模拟C语言的结构体。必须注意, Structure子类中的公共字段的顺序,必须与C语言中的结构的顺序一致。否则会报错!因为,Java调用动态链接库中的C函数,实际上就是一段内存作为函数的参数传递给C函数。动态链接库以为这个参数就是C语言传过来的参数同时,C语言的结构体是一个严格的规范,它定义了内存的次序。因此,JNA中模拟的结构体的变量顺序绝对不能错如果一个 Struct有2个int变量。Inta,intb如果JNA中的次序和C中的次序相反,那么不会报错,但是数据将会被传递到错误的段中去。Structure类代表」一个原生结构体。当 Structure对象作为一个函数的参数或者返回值传递时,它代表结构体指针。当它被用在另一个结构体内部作为一个字段时,它代表结构体木身另外, Structure类有两个内部接口 Structure. ByReference和 Structure. ByValue。这两个接口仅仅是标记,如果一个类实现 Structure. By Reference接口,就表小这个类代表结构体指针。如果一个类实现 Structure. ByValue接口,就表示这个类代表结构体本身。使用这两个接口的实现类,可以明确定义我们的 Structure实例表示的是结构体的指针还是结构体本身。上面的例子中,由于 Structure实例作为函数的参数使用,因此是结构体指针。所以这里直接使用了 Userstructuserstruct= new Userstruct();也可以使用 Userstruct userstruct= new Userstruct. ByReference()明确指出 userstruct对象是结构体指针而不是结构体本身。JNA模拟复杂结构体C诘言最主要的数据类型就是结构体。结构体可以内部可以嵌套结构体,这使它可以模拟任何类型的对象JNA也可以模拟这类复杂的结构体。例3结构体内部可以包含结构体对象的数组struct CompanyStructng 1wchar ty nameLserStruct users[100int counJNA中可以这样模拟public static class Companystruct extends structure ib1主 c Nativelong 1C;ublic WString nlainepub1⊥cUserstruct ByValue []users=newUserstruct ByValue[100]ipublic int counti这里,必须给 users字段赋值,否则不会分配100个 UserStruct结构体的内存,这样JNA中的内存大小和原生代码中结构体的内存大小不一致,调用就会失败。例4结构体内部可以包含结构体对象的指针的数组struct Company Struct2 tlong idwchar t*k nameUserStruct** users 100Int countJNA中可以这样模拟:public static class CompanyStruct2 extends structure ipublic Nativelong id;public WStriny nainepublicUserstruct ByReference[]users=newUserstruct ByReference[100]ib1⊥c⊥ nt count测试代码:Companystruct2 ByReferencecompanystruct2-newCompany struct ByReference()icompanystruct2.id=new NativeLong(2)icompanyStruct2. name=new wString("Yahoo")icompanystruct2. count=10UserStruct ByReferencepUserstruct=newUserstruct ByReference(pUserstruct. id=new Nativelong(90)ipUserstruct. age=99iUserstruct.ane= new astring("杨致远")/ pUserstruct write()ifor(int i=0: i](基本类型的数组)pointer32或64指针(参数/返回值array邻接内存(结构体成员)PointerTypeointer|和 Pointer相同原生代码中的数组,可以使用JNA中对应类型的数组来表小原生代码中的指针,可以使用 Pointer类型,或者 Pointer Type类型及它们的子类型来模拟。Pointer代表原生代码中的指针。其属性per就是原生代码中指针的地址。我们不可以直接创建 Pointer对象,但可以用它表示原生函数中的任何指针。Pointer类有2个子类: Function, Memory。Function类代表原生函数的指针,可以通过 invoke( Class, Object[],Map)这一系列的方法调用原生函数。Memory类代表的是堆中的一段内存,它也是我们可以创建的 Painter子类。创建一个Memory类的实例,就是在原生代码的内存区中分配一块指定人小的内存。这块内存会在GC释放这个Java对象吋被释放。 Memory类在指针模拟中会被经常用到Pointer Type类代表的是一个类型安全的指针。 ByReference类是 PointerType类的子类。 ByReference类代表指向堆内存的指针。 By Reference类非常简单。public abstract class ByRefererce extends PointerTypeprotected ByReference (int datasize)setFcirter(new Memcry(datasize))iByReference类有很多了类,这些类都非常有用。BytcBy Rcfcrence, DoublcByRefcrencc, FloatBy Rcfcrence, IntByRefcrencc,LongByReference, NativeLong By Reference, PointerByReference, ShortByReferenceW32API. HANdLEBy Reference, Xll. AtomBy Reference, Xll. Window By ReferenceByteBy reference等类故名思议,就是指向原生代码中的字节数据的指针。PointerBy Reference类表示指向指针的指针。在JMA中模拟指针,最常用到的就是 Pointer类和 PointerBy reference类。 Pointer类代表指向任何东西的指针, Pointerbyreference类表示指向指针的指针。 Pointer类更加通用,事实上 PointerBy reference类内部也持有 Pointer类的实例Pointer By reference类可以嵌套使用,它所指向的指针,本身可能也是指向指针的指针。 Pointer By Reference类的源代码:public class PointerByReference extends ByReference ipublic PointerByReference()this(null)ipublic PointerByReference(Pointer value)super(Pointer.SIZE);setvalue(value)iublic void setvalue(Pointer value)fgetPcirter(). setpointer(o, value)ipublic Pointer getvalue()return getPointer().getPointer(o)i可以看到, PointerByReference类的构造器做了如下工作:
用户评论