logo资料库

Jna详细教程.pdf

第1页 / 共13页
第2页 / 共13页
第3页 / 共13页
第4页 / 共13页
第5页 / 共13页
第6页 / 共13页
第7页 / 共13页
第8页 / 共13页
资料共13页,剩余部分请下载后查看
为什么需要JNA
JNA介绍
JNA实现原理
JNA调用原生函数
例子1使用JNA调用原生函数
使用JNA调用原生函数的模式
Java和原生代码的类型映射
Java—C和操作系统数据类型的对应表
JNA支持常见的数据类型的映射
跨平台、跨语言调用原则:
JNA模拟结构体
例2使用JNA调用使用Struct的C函数
Structure说明
JNA模拟复杂结构体
例3结构体内部可以包含结构体对象的数组
例4结构体内部可以包含结构体对象的指针的数组
原生代码调用Java代码
例5通过回调函数实现原生代码调用Java代码
JNA回调函数说明
JNA模拟指针
例6使用PointerByReference模拟指向指针的指针
例7使用Pointer和PointerByReference模拟指针
Pointer类详解
结语
深入浅出 JNAJNAJNAJNA————快速调用原生函数 By 沈东良(网名:良少) Blog: http://blog.csdn.net/shendl 2009/7/20 本文原名《使用 JNA 方便地调用原生函数》发表于 2009 年 3 月的“程序员”杂 志 上 。 感谢程序员杂志的许可,使这篇文章能够成为免费的电子版,发布于网络上。 程序员杂志发表此文时,略有裁剪,因此本文比程序员上的文章内容更多。 JNA 的 API 参考手册和最新版本的 pdf 文档,可以在如下地址下载: http://code.google.com/p/shendl/downloads/list 目录 深入浅出 JNA—快速调用原生函数................................................................................................1 为什么需要 JNA............................................................................................................................... 2 JNA 介绍........................................................................................................................................... 2 JNA 实现原理................................................................................................................................... 2 JNA 调用原生函数................................................................................................................... 3 例子 1 使用 JNA 调用原生函数................................................................................3 使用 JNA 调用原生函数的模式.......................................................................................3 Java 和原生代码的类型映射...........................................................................................................4 Java—C 和操作系统数据类型的对应表................................................................................. 4 JNA 支持常见的数据类型的映射........................................................................................... 4 跨平台、跨语言调用原则:...................................................................................................5 JNA 模拟结构体............................................................................................................................... 5 例 2 使用 JNA 调用使用 Struct 的 C 函数..........................................................................5 Structure 说明.................................................................................................................. 6 JNA 模拟复杂结构体............................................................................................................... 7 例 3 结构体内部可以包含结构体对象的数组......................................................... 7 例 4 结构体内部可以包含结构体对象的指针的数组............................................. 7 原生代码调用 Java 代码..................................................................................................................8 例 5 通过回调函数实现原生代码调用 Java 代码............................................................ 8 JNA 回调函数说明................................................................................................................... 9 JNA 模拟指针................................................................................................................................... 9 例 6 使用 PointerByReference 模拟指向指针的指针..................................................... 11 例 7 使用 Pointer 和 PointerByReference 模拟指针..................................................... 11 Pointer 类详解....................................................................................................................... 12 结语................................................................................................................................................ 13
为什么需要 JNAJNAJNAJNA 和许多解释执行的语言一样,Java 提供了调用原生函数的机制,以加强 Java 平台的能 力。Java™ Native Interface (JNI)就是 Java 调用原生函数的机制。 事实上,很多 Java 核心代码内部就是使用 JNI 实现的。这些 Java 功能实际上是通过原 生函数提供的。 但是,使用 JNI 对 Java 开发者来说简直是一场噩梦。 如果你已经有了原生函数,使用 JNI,你必须使用 C 语言再编写一个动态链接库,这个 动态链接库的唯一功能就是使用 Java 能够理解的 C 代码来调用目标原生函数。 这个没什么实际用途的动态链接库的编写过程令人沮丧。同时编写 Java 和 C 代码使开 发难度大大增加。 因此,在 Java 开发社区中,人们一直都视 JNI 为禁地,轻易不愿涉足。 缺少原生函数的协助使 Java 的使用范围大大缩小。 反观.NET 阵营,其 P/Invoke 技术调用原生函数非常方便,不需要编写一行 C 代码,只 需要写 Annotation 就可以快速调用原生函数。因此,与硬件有关的很多开发领域都被.NET 所占据。 JNAJNAJNAJNA 介绍 JNA(Java Native Access)框架是一个开源的 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。 JNA 使 Java 平台可以方便地调用原生函数,这大大扩展了 Java 平台的整合能力。 JNAJNAJNAJNA 实现原理 JNI 是 Java 调用原生函数唯一的机制。JNA 也是建立在 JNI 技术之上的。它简化了 Java
调用原生函数的过程。 JNA 提供了一个动态的 C 语言编写的转发器,可以自动实现 Java 和 C 的数据类型映射。 你不再需要编写那个烦人的 C 动态链接库。 当然,这也意味着,使用 JNA 技术比使用 JNI 技术调用动态链接库会有些微的性能损失 。 可能速度会降低几倍。但对于绝大部分项目来说,影响不大。 JNAJNAJNAJNA 调用原生函数 让我们先看一个 JNA 调用原生函数的例子。 例子 1111 使用 JNAJNAJNAJNA 调用原生函数 假设我们有一个动态链接库,发布了这样一个 C 函数: void say(wchar_t* pValue){ std::wcout.imbue(std::locale("chs")); std::wcout<
载入进来。使用 JNA,我们不需要编写作为代理的动态链接库,不需要编写一行原生代码。 上面的 JNA 代码使用了单例,接口的静态变量返回的是接口的唯一实例,这个 Java 对 象是 JNA 通过反射动态创建的。通过这个对象,我们可以调用动态链接库发布的函数。 JavaJavaJavaJava 和原生代码的类型映射 跨平台、跨语言调用的最大难点,就是不同语言之间数据类型不一致造成的问题。绝大 部分跨平台调用的失败,都是这个问题造成的。 JNA 使用的数据类型是 Java 的数据类型。而原生函数中使用的数据类型是原生函数的编 程语言使用的数据类型。可能是 C,Delphi,汇编等语言的数据类型。因此,不一致是在所难免 的。 JNA 提供了 Java 和原生代码的类型映射。 JavaJavaJavaJava————CCCC 和操作系统数据类型的对应表 JavaJavaJavaJava 类型 boolean byte char short int long float double Buffer Pointer [] (基本类型的数组) CCCC 类型 int char wchar_t short int 原生表现 32 位整数 (可定制) 8 位整数 平台依赖 16 位整数 32 位整数 long long, __int64 64 位整数 float double pointer pointer array 32 位浮点数 64 位浮点数 平台依赖(32 或 64 位指针) 32 或 64 位指针(参数/返回值) 邻接内存(结构体成员) JNAJNAJNAJNA 支持常见的数据类型的映射 JavaJavaJavaJava 类型 CCCC 类型 原生表现 String char* \0 结束的数组 (native encoding or jna.encoding) WString String[] wchar_t* \0 结束的数组(unicode) char** \0 结束的数组的数组
WString[] wchar_t** \0 结束的宽字符数组的数组 Structure struct* struct 指向结构体的指针 (参数或返回值) (或者明确指定是结构体指 针) 结构体(结构体的成员) (或者明确指定是结构体) Union union 等同于结构体 Structure[] struct[] 结构体的数组,邻接内存 Callback (*fp)() Java 函数指针或原生函数指针 NativeMapped varies NativeLong PointerType 依赖于定义 平台依赖(32 或 64 位整数) long pointer 和 Pointer 相同 跨平台、跨语言调用原则: 尽量使用基本、简单的数据类型; 尽量少跨平台、跨语言传递数据! 如果有复杂的数据类型需要在 Java 和原生函数中传递,那么我们就必须在 Java 中模拟 大量复杂的原生类型。这将大大增加实现的难度,甚至无法实现。 如果在 Java 和原生函数间存在大量的数据传递,那么一方面,性能会有很大的损失。 更为重要的是,Java 调用原生函数时,会把数据固定在内存中,这样原生函数才可以访问这 些 Java 数据。这些数据,JVM 的 GC 不能管理,会造成内存碎片。 如果在你需要调用的动态链接库中,有复杂的数据类型和庞大的跨平台数据传递。那么 你应该另外写一些原生函数,把需要传递的数据类型简化,把需要传递的数据量简化。 JNAJNAJNAJNA 模拟结构体 在原生代码中,结构体是经常使用的复杂数据类型。这里我们研究一下怎样使用 JNA 模拟结构体。 Struct 例 2222 使用 JNAJNAJNAJNA 调用使用 Struct Struct 的 CCCC 函数 Struct 假设我们现在有这样一个 C 语言结构体 struct UserStruct{ long id; wchar_t* name; int age; }; 使用上述结构体的函数 #define MYLIBAPI extern "C" __declspec( dllexport )
MYLIBAPI void sayUser(UserStruct* pUserStruct); 对应的 Java 程序中,在例 1 的 接口中添加下列代码: extends class static public public static class extends static class public static public class UserStruct extends extends Structure{ public public public public NativeLong id; public public public public WString name; public intintintint age; public public public public static public static public public static static implements implements implements implements Structure.ByReference { } extends class static public implements public static class extends implements extends UserStruct implements class ByValue extends static class public static public implements Structure.ByValue ByReference class class class class extends extends extends extends UserStruct { } } public public public public void voidvoidvoid sayUser(UserStruct.ByReference struct); Java 中的调用代码: UserStruct userStruct=newnewnewnew UserStruct (); userStruct.id=newnewnewnew NativeLong(100); userStruct.age=30; userStruct.name=newnewnewnew WString("奥巴马"); TestDll1.INSTANCE.sayUser(userStruct); Structure Structure Structure Structure 说明 现在,我们就在 Java 中实现了对 C 语言的结构体的模拟。 这里,我们继承了 Structure 类,用这个类来模拟 C 语言的结构体。 必须注意,Structure 子类中的公共字段的顺序,必须与 C 语言中的结构的顺序一致。 否则会报错! 因为,Java 调用动态链接库中的 C 函数,实际上就是一段内存作为函数的参数传递给 C 函数。 动态链接库以为这个参数就是 C 语言传过来的参数。 同时,C 语言的结构体是一个严格的规范,它定义了内存的次序。因此,JNA 中模拟的 结构体的变量顺序绝对不能错。 如果一个 Struct 有 2 个 int 变量。 Int a, int b 如果 JNA 中的次序和 C 中的次序相反,那么不会报错,但是数据将会被传递到错误的 字段中去。 Structure 类代表了一个原生结构体。当 Structure 对象作为一个函数的参数或者返回 值传递时,它代表结构体指针。当它被用在另一个结构体内部作为一个字段时,它代表结构 体本身。 另外,Structure 类有两个内部接口 Structure.ByReference 和 Structure.ByValue。这两个接 口仅仅是标记,如果一个类实现 Structure.ByReference 接口,就表示这个类代表结构体指针 。 如果一个类实现 Structure.ByValue 接口,就表示这个类代表结构体本身。 使用这两个接口的实现类,可以明确定义我们的 Structure 实例表示的是结构体的指针 还是结构体本身。 上面的例子中,由于 Structure 实例作为函数的参数使用,因此是结构体指针。所以这 里直接使用了 UserStruct userStruct=newnewnewnew UserStruct ();
也可以使用 UserStruct userStruct=newnewnewnew UserStruct.ByReference (); 明确指出 userStruct 对象是结构体指针而不是结构体本身。 JNAJNAJNAJNA 模拟复杂结构体 C 语言最主要的数据类型就是结构体。结构体可以内部可以嵌套结构体,这使它可以模 拟任何类型的对象。 JNA 也可以模拟这类复杂的结构体。 例 3333 结构体内部可以包含结构体对象的数组 struct CompanyStruct{ long id; wchar_t* name; UserStruct users[100]; int count; }; JNA 中可以这样模拟: extends class static public public static class extends class CompanyStruct extends static class public static public extends Structure{ UserStruct.ByValue[] users=newnewnewnew public public public public NativeLong id; public public public public WString name; public public public public UserStruct.ByValue[100]; public public intintintint count; public public } 这里,必须给 users 字段赋值,否则不会分配 100 个 UserStruct 结构体的内存,这样JNA 中的内存大小和原生代码中结构体的内存大小不一致,调用就会失败。 例 4444 结构体内部可以包含结构体对象的指针的数组 struct CompanyStruct2{ long id; wchar_t* name; UserStruct* users[100]; int count; }; JNA 中可以这样模拟: extends class static public public static class extends class CompanyStruct2 extends static class public static public extends Structure{ public public public public NativeLong id; public public public public WString name; public public public public UserStruct.ByReference[100]; UserStruct.ByReference[] users=newnewnewnew
public public intintintint count; public public } 测试代码: CompanyStruct2.ByReference CompanyStruct2.ByReference(); companyStruct2.id=newnewnewnew NativeLong(2); companyStruct2.name=newnewnewnew WString("Yahoo"); companyStruct2.count=10; UserStruct.ByReference companyStruct2=newnewnewnew pUserStruct=newnewnewnew UserStruct.ByReference(); pUserStruct.id=newnewnewnew NativeLong(90); pUserStruct.age=99; pUserStruct.name=newnewnewnew WString("杨致远"); // pUserStruct.write(); forforforfor(intintintint i=0;i
分享到:
收藏