




已阅读5页,还剩10页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
如何在如何在 C C 中调用中调用 Java 减小字体 增大字体 作者 佚名 来源 本站整理 发布时间 2010 02 15 22 34 40 瓦宝设计为你提供网站制作全面的教程 请记住我们的域名 CN Java 跨平台的特性使 Java 越来越受开发人员的欢迎 但也往往会听到不少的抱怨 用 Java 开发的 图形用户窗口界面每次在启动的时候都会跳出 个控制台窗口 这个控制台窗口让本来非常棒的界面失色 不少 怎么能够让通过 Java 开发的 GUI 程序不弹出 Java 的控制台窗口呢 其实现在很多流行的开发环 境例如 JBuilder Eclipse 都是使用纯 Java 开发的集成环境 这些集成环境启动的时候并不会打开 个 命令窗口 因为它使用了 JNI Java Native Interface 的技术 通过这种技术 开发人员不 定要用 命令行来启动 Java 程序 可以通过编写 个本地 GUI 程序直接启动 Java 程序 这样就可避免另外打开 个命令窗口 让开发的 Java 程序更加专业 JNI 答应运行在虚拟机的 Java 程序能够与其它语言 例如 C 和 C 编写的程序或者类库进行相互间 的调用 同时 JNI 提供的 整套的 API 答应将 Java 虚拟机直接嵌入到本地的应用程序中 图 1 是 Sun 站点上对 JNI 的基本结构的描述 图 1 JNI 基本结构描述图 本文将介绍如何在 C C 中调用 Java 方法 并结合可能涉及到的问题介绍整个开发的步骤及可能碰到 的难题和解决方法 本文所采用的工具是 Sun 公司创建的 Java Development Kit JDK 版本 1 3 1 以及微软公司的 Visual C 6 开发环境 环境搭建环境搭建 为了让本文以下部分的代玛能够正常工作 我们必须建立 个完整的开发环境 首先需要下载并安装 JDK 1 3 1 其下载地址为 假设安装路径为 C JDK 下 步就是设置集成 开发环境 通过 Visual C 6 的菜单 Tools Options 打开选项对话框如图 2 图 2 设置集成开发环境图 将目录 C JDK include 和 C JDK include win32 加入到开发环境的 Include Files 目录中 同时将 C JDK lib 目录添加到开发环境的 Library Files 目录中 这 个目录是 JNI 定义的 些常量 结构及方 法的头文件和库文件 集成开发环境已经设置完毕 同时为了执行程序需要把 Java 虚拟机所用到的动态 链接库所在的目录 C JDK jre bin classic 设置到系统的 Path 环境変量中 这里需要提出的是 某些 开发人员为了方便直接将 JRE 所用到的 DLL 文件直接拷贝到系统目录下 这样做是不行的 将导致初始 化 Java 虚拟机环境失败 返回值 1 原因是 Java 虚拟机是以相对路径来寻找所用到的库文件和其它 些相关文件的 至此整个 JNI 的开发环境设置完毕 为了让此次 JNI 旅程能够顺利进行 还必须先预 备 个 Java 类 在这个类中将用到 Java 中几乎所有有代表性的属性及方法 如静态方法与属性 数组 异常抛出与捕捉等 我们定义的 Java 程序 Demo java 如下 本文中所有的代玛演示都将基于该 Java 程序 代玛如下 package jni test 该类是为了演示 JNI 如何访问各种对像属性等 author liudong public class Demo 用于演示如何访问静态的基本类型属性 public static int COUNT 8 演 示对像型属性 public String msg private int counts public Demo this 缺省构造函数 演示如何访问构造器 public Demo String msg System out println msg this msg msg this counts null 该方法演示如何访问 个访问以及中文字符的处理 public String getMessage return msg 演示数组对像的访问 public int getCounts return counts 演示如何构造 个数组对像 public void setCounts int counts this counts counts 演示异常的捕捉 public void throwExcp throws IllegalAccessException throw new IllegalAccessException exception occur 初始化虚拟机初始化虚拟机 本地代玛在调用 Java 方法之前必须先加载 Java 虚拟机 而后所有的 Java 程序都在虚拟机中执行 为 了初始化 Java 虚拟机 JNI 提供了 系列的接口函数 Invocation API 通过这些 API 可以很方便地将 虚拟机加载到内存中 创建虚拟机可以用函数 jint JNI CreateJavaVM JavaVM pvm void penv void args 对于这个函数有 点需要注重的是 在 JDK 1 1 中第 个参数总是指向 个结 构 JDK1 1InitArgs 这个结构无法完全在所有版本的虚拟机中进行无缝移植 在 JDK 1 2 中已经使 用了 个标准的初始化结构 JavaVMInitArgs 来替代 JDK1 1InitArgs 下面我们分别给出两种不同版 本的示例代玛 在 JDK 1 1 初始化虚拟机 include int main JNIEnv env JavaVM jvm JDK1 1InitArgs vm args jint res IMPORTANT 版本号设置 定不能漏 vm args version 0 x00010001 获取缺省的虚拟机初 始化参数 JNI GetDefaultJavaVMInitArgs 添加自定义的类路径 sprintf classpath s c s vm args classpath PATH SEPARATOR USER CLASSPATH vm args classpath classpath 设置 些其他的初始化参数 创建虚拟机 res JNI CreateJavaVM if res DestroyJavaVM jvm JDK 1 2 初始化虚拟机 invoke2 c include int main int res JavaVM jvm JNIEnv env JavaVMInitArgs vm args JavaVMOption options 3 vm args version JNI VERSION 1 2 这个字段必须设 置为该值 设置初始化参数 options 0 optionString Dpiler NONE options 1 optionString Djava class path options 2 optionString verbose jni 用于跟踪运 行时的信息 版本号设置不能漏 vm args version JNI VERSION 1 2 vm args nOptions 3 vm args options options vm args ignoreUnrecognized JNI TRUE res JNI CreateJavaVM if res DestroyJavaVM jvm fprintf stdout Java VM destory n 为了保证 JNI 代玛的可移植性 建议使用 JDK 1 2 的方法来创建虚拟机 JNI CreateJavaVM 函数的第 个参数 JNIEnv env 就是贯穿整个 JNI 始末的 个参数 因为几乎所有的函数都要求 个参数就是 JNIEnv env 访问类方法访问类方法 初始化了 Java 虚拟机后 就可以开始调用 Java 的方法 要调用 个 Java 对像的方法必须经过几个步 骤 1 获取指定对像的类定义 jclass 有两种途径来获取对像的类定义 第 种是在已知类名的情况下使用 FindClass 来查找对应的类 但是 要注重类名并不同于平时写的 Java 代玛 例如要得到类 jni test Demo 的定义必须调用如下代玛 jclass cls env FindClass env jni test Demo 把点号换成斜杠 然后通过对像直接得到其所对应的类定义 jclass cls env GetObjectClass env obj 其中 obj 是要引用的对像 类型是 jobject 2 读取要调用方法的定义 jmethodID 我们先来看看 JNI 中获取方法定义的函数 jmethodID JNICALL GetMethodID JNIEnv env jclass clazz const char name const char sig jmethodID JNICALL GetStaticMethodID JNIEnv env jclass class const char name const char sig 这两个函数的区别在于 GetStaticMethodID 是用来获取静态方法的定义 GetMethodID 则是获取非静 态的方法定义 这两个函数都需要提供 个参数 env 就是初始化虚拟机得到的 JNI 环境 第 个参数 class 是对像的类定义 也就是第 步得到的 obj 第 个参数是方法名称 最重要的是第 个参数 这 个参数是方法的定义 因为我们知道 Java 中答应方法的多态 仅仅是通过方法名并没有办法定位到 个 具体的方法 因此需要第 个参数来指定方法的具体定义 但是怎么利用 个字符串来表示方法的具体定 义呢 JDK 中已经预备好 个反编译工具 javap 通过这个工具就可以得到类中每个属性 方法的定义 下面就来看看 jni test Demo 的定义 打开命令行窗口并运行 javap s p jni test Demo 得到运行结果如下 Compiled from Demo java public class jni test Demo extends java lang Object public static int COUNT I public java lang String msg Ljava lang String private int counts I public jni test Demo V public jni test Demo java lang String Ljava lang String V public java lang String getMessage Ljava lang String public int getCounts I public void setCounts int I V public void throwExcp throws java lang IllegalAccessException V static V 我们看到类中每个属性和方法下面都有 段注释 注释中不包含空格的内容就是第 个参数要填的内容 关于 javap 具体参数请查询 JDK 的使用帮助 下面这段代玛演示如何访问 jni test Demo 的 getMessage 方法 假设我们已经有 个 jni test Demo 的实例 obj jmethodID mid jclass cls env GetObjectClass env obj 获取实例的类定义 mid env GetMethodID env cls getMessage Ljava lang String 假如 mid 为 0 表示获取方法 定义失败 jstring msg env CallObjectMethod env obj mid 假如该方法是静态的 方法那只需要将最后 句代玛改为以下写法即可 jstring msg env CallStaticObjectMethod env cls mid 3 调用方法 为了调用对像的某个方法 可以使用函数 CallMethod 或者 CallStaticMethod 访问类的静态方法 根据不同的返回类型而定 这些方法都是使用可变参数的定义 假如访问某个方法需要参数时 只需要把 所有参数按照顺序填写到方法中就可以 在讲到构造函数的访问时 将演示如何访问带参数的构造函数 访问类属性访问类属性 访问类的属性与访问类的方法大体上是 致的 只不过是把方法变成属性而已 1 获取指定对像的类 jclass 这 步与访问类方法的第 步完全相同 具体使用参看访问类方法的第 步 2 读取类属性的定义 jfieldID 在 JNI 中是这样定义获取类属性的方法的 jfieldID JNICALL GetFieldID JNIEnv env jclass clazz const char name const char sig jfieldID JNICALL GetStaticFieldID JNIEnv env jclass clazz const char name const char sig 这两个函数中第 个参数为 JNI 环境 clazz 为类的定义 name 为属性名称 第 个参数同样是为了表 达属性的类型 前面我们使用 javap 工具获取类的具体定义的时候有这样两行 public java lang String msg Ljava lang String 其中第 行注释的内容就是第 个参数要填的信息 这跟访问类方法时是相同的 3 读取和设置属性值 有了属性的定义要访问属性值就很轻易了 有几个方法用来读取和设置类的属性 它们是 GetField SetField GetStaticField SetStaticField 比如读取 Demo 类的 msg 属性就可以用 GetObjectField 而访问 COUNT 用 GetStaticIntField 相关代玛如下 jfieldID field env GetFieldID env obj msg Ljava lang String jstring msg env GetObjectField env cls field msg 就是对应 Demo 的 msg jfieldID field2 env GetStaticFieldID env obj COUNT I jint count env GetStaticIntField env cls field2 访问构造函数访问构造函数 很多人刚刚接触 JNI 的时候往往会在这 节碰到问题 查遍了整个 jni h 看到这样 个函数 NewObject 它应该是可以用来访问类的构造函数 但是该函数需要提供构造函数的方法定义 其类型 是 jmethodID 从前面的内容我们知道要获取方法的定义首先要知道方法的名称 但是构造函数的名称 怎么来填写呢 其实访问构造函数与访问 个普通的类方法大体上是 样的 惟 不同的只是方法名称不 同及方法调用时不同而已 访问类的构造函数时方法名必须填写 下面的代玛演示如何构造 个 Demo 类的实例 jclass cls env FindClass env jni test Demo 首先通过类的名称获取类的定义 相 当于 Java 中的 Class forName 方法 if cls 0 jmethodID mid env GetMethodID env cls Ljava lang String V if mid 0 jobject demo jenv NewObject cls mid 0 访问构造函数必须使用 NewObject 的函数来调用前面获取的构造函数 的定义 上面的代玛我们构造了 个 Demo 的实例并传 个空串 null 数组处理数组处理 创建 个新数组 要创建 个数组 我们首先应该知道数组元素的类型及数组长度 JNI 定义了 批数组的类型 jArray 及 数组操作的函数 NewArray 其中就是数组中元素的类型 例如 要创建 个大小为 10 并且每个位置值 分别为 1 10 的整数数组 编写代玛如下 int i 1 jintArray array 定义数组对像 env NewIntArray env 10 for iSetIntArrayRegion env array i 1 1 访问数组中的数据 访问数组首先应该知道数组的长度及元素的类型 现在我们把创建的数组中的每个元素值打印出来 代玛 如下 int i 获取数组对像的元素个数 int len env GetArrayLength env array 获取数 组中的所有元素 jint elems env GetIntArrayElements env array 0 for i 0 iGetStringChars str 0 env ReleaseStringChars str w buffer ZeroMemory desc desc len 调用字符编码转换函数 Win32 API 将 UNICODE 转为 ASCII 编码格式字符串 关于函数 WideCharToMultiByte 的使用请 参考 MSDN len WideCharToMultiByte CP ACP 0 w buffer 1024 desc desc len NULL NULL len wcslen w buffer if len 0 int slen strlen str jchar buffer new jchar slen int len MultiByteToWideChar CP ACP 0 str strlen str buffer slen if len 0 delete buffer return js 异常异常 由于调用了 Java 的方法 因此难免产生操作的异常信息 这些异常没有办法通过 C 本身的异常处理 机制来捕捉到 但 JNI 可以通过 些函数来获取 Java 中抛出的异常信息 之前我们在 Demo 类中定义 了 个方法 throwExcp 下面将访问该方法并捕捉其抛出来的异常信息 代玛如下 假设我们已经构造了 个 Demo 的实例 obj 其类定义为 cls jthrowable excp 0 异常信 息定义 jmethodID mid env GetMethodID env cls throwExcp V 假如 mid 为 0 表示获取方法定义失败 jstring msg env CallVoidMethod env obj mid 在调用 该方法后会有 个 IllegalAccessException 的异常抛出 excp env ExceptionOccurred env if excp env ExceptionClear env 通过访问 excp 来获取具 体异常信息 在 Java 中 大部分的异常信息都是扩展类 java lang Exception 因此可以访问 excp 的 toString 或者 getMessage 来获取异常信息的内容 访问这两个方法同前面讲到的如何访问类的方 法是相同的 线程和同步访问线程和同步访问 有些时候需要使用多线程的方式来访问 Java 的方法 我们知道 个 Java 虚拟机是非常消耗系统的内存 资源 差不多每个虚拟机需要内存大约在 20MB 左右 为了节省资源要求每个线程使用的是同 个虚拟 机 这样在整个的 JNI 程序中只需要初始化 个虚拟机就可以了 所有人都是这样想的 但是 旦子线程 访问主线程创建的虚拟机环境変量 系统就会出现错误对话框 然后整个程序终止 其实这里面涉及到两个概念 它们分别是虚拟机 JavaVM jvm 和虚拟机环境 JNIEnv env 真正 消耗大量系统资源的是 jvm 而不是 env jvm 是答应多个线程访问的 但是 env 只能被创建它本身的线 程所访问 而且每个线程必须创建自己的虚拟机环境 env 这时候会有人提出疑问 主线程在初始化虚拟 机的时候就创建了虚拟机环境 env 为了让子线程能够创建自己的 env JNI 提供了两个函数 AttachCurrentThread 和 DetachCurrentThread 下面代玛就是子线程访问 Java 方法的框架 DWord WINAPI ThreadProc PVOID dwParam JavaVM jvm JavaVM dwParam 将虚 拟机通过参数传入 JNIEnv env jvm AttachCurrentThread jvm void jvm DetachCurrentThread jvm 时间时间 关于时间的话题是我在实际开发中碰到的 个问题 当要发布使用了 JNI 的程序时 并不 定要求客户要 安装 个 Java 运行环境 因为可以在安装程序中打包这个运行环境 为了让打包程序利于下载 这个包 要比较小 因此要去除 JRE Java 运行环境 中 些不必要的文件 但是假如程序中用到 Java 中的日 历类型 例如 java util Calendar 等 那么有个文件 定不能去掉 这个文件就是 JRE lib tzmappings 它是 个时区映射文件 旦没有该文件就会发现时间操作上经常出现与正确时间相 差几个小时的情况 下面是打包 JRE 中必不可少的文件列表 以 Windows 环境为例 其中 JRE 为运行 环境的目录 同时这些文件之间的相对路径不能变 文件名目录 hpi dll JRE bin ioser12 dll JRE bin java dll JRE bin net dll JRE bin verify dll JRE bin zip dll JRE bin jvm dll JRE bin classic rt jar JRE lib tzmappings JRE lib 由于 rt jar 有差不多 10MB 但是其中有很大 部分文件并不需要 可以根据实际的应用情况进行删除 例如程序假如没有用到 Java Swing 就可以把涉及到 Swing 的文件都删除后重新打包 浅谈浅谈 C 与与 Java 混合编程混合编程 现实的情况是 真实的项目中 通常是涉及多种编程语言 举几个简单的例子 一个软件 为了快速开发 可能是使用 delphi 或 vb 作为界面开发首选语言 底层的指令或核心算法 会使用 c c 处理 涉及数据处理的时候 为了安全和快速开发 会使用 javascript 或 p ython 等脚本语言实现数据分析处理 因此 开发者应该学习或掌握语言混合编程 c 和 java 是主流的两种编程语言 但是现在整个网上对实现这两种语言混合编程的资料少之 又少 却又说之不全 并且有时多种问题现在也含糊不清 对正在学习或使用这两种语言 的朋友造成很大的困扰 本人的这篇拙作 希望对使用这两种语言混合编程学习的朋友可 以抛砖引玉 实现原理 实现 java 和 c 的交互 使用的技术是称为 jni java native interface c 编写的 程序 只要实现 jni 生成的接口 则可以让 java 程序调用 而 java 编写的程序 c 调用 则需要运行 java 虚拟机 通过 jni 查询调用 java 实现的方法 环境变量设置 本文中使用的 java 的版本是 build 1 6 0 03 b05 c 的版本为 vc 6 0 版本 并根 据你本机上的 java 和 c 安装目录设置以下的环境变量 注意不要缺少 java 的 include 和 lib 这三个红线标出部分 为源码包文件中的 cpp env bat 这个批处理文件 设置 java 的环境变量 如下图所示 注意红线标注的这处部分 这部分与 c 调用 java 的方法时候影响非常重要 为源码包文 件中的 java env bat 这个批处理文件 在 command 模式运行这两个批处理文件后 就 可以在 command 模式运行 demo 程序了 java 调用 c 的方法 源码文件中 src java cpp 目录中的 winfile java 的这个文件 java 语法规定类名与 文件名必须一致 定义了一个 winfile 类 这个类的内容如下 在代码的第 18 行 声明一个带 native 属性的方法 getfilesfromdir 这个方法传入一个字符 类参数 并返回一个字符类参数 而 system loadlibrary 则会加载指定的共享链接库 参数 所示加载的动态库为 libwinfile dll 在 windows 平台上 执行时会自动加入后缀 dll 在 c ommand 模式运行以下命令 第一条命令则会生成 winfile class 的编译文件 而第二条命令则会生成 winfile h 这个头文 件 这个头文件包含了 winfile java 中的 native 的方法的 c c 语言的定义 在 c c 的语言定中 java 语言的 string 的定义为 jstring 注意 java 的语言的字符与程 序的编码都是以 utf 8 编码实现的 所以 java 中的中文字符在 c 的方法中如果没有编码 转换 则会显示为乱码 同理 在 c 的方法中将中文字符返回给 java 如果没有将字符 编码转为 utf 8 在 java 的方法显示同样会是乱码 以上为 src java cpp winfile cpp 的部分代码 代码中实现了两个函数 一个是将 utf
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 智能轴承市场格局-洞察及研究
- 智能巡更系统行业深度研究分析报告(2024-2030版)
- 废水废气环保处理设备生产线项目可行性研究报告
- 供水管道工上岗证考试题库及答案
- 结构静强度环境测试工岗位实习报告
- 数控车工基础技能培训手册
- 合成洗涤剂制造工岗位实习报告
- 2025年中国紫外固化光纤涂覆材料未来趋势预测分析及投资规划研究建议报告
- 蛋类制品加工工岗位实习报告
- 用电客户受理员职业技能鉴定经典试题含答案
- 《电工电子技术》课程标准
- 陈案清查工作报告
- 舞台租赁协议模板与舞台设备租赁合同3篇
- 医生患者和解协议书
- 东莞住宅工程质量通病防治手册
- 2025-2030年中国海洋大数据行业市场现状供需分析及投资评估规划分析研究报告
- 2025新人教版英语八上单词英译汉默写表(先鸟版)
- 药店借用资质协议书范本
- DB34T 4676-2024数字茶园建设指南
- 二维材料光电子器件集成-全面剖析
- 建筑项目主要劳动力配置计划
评论
0/150
提交评论