例解 VC++ 6.0 实现 JNI
http://gceclub.sun.com.cn/yuanchuang/week-15/jni.html
作者:陈健明
作者简介
陈健明,华中师范大学网络与通讯研究所,您可以通过chenjm2000@hotmail.com和作者取得联系。
内容摘要
JNI是JDK的一部分,用于为Java提供一个本地代码的接口。通过使用JNI编写的程序能够确保你的代码能够完全的移植到所有的平台。JNI使得运行在JVM虚拟机上的Java代码能够操作使用其它语言编写的应用程序和库,比如C/C++以及汇编语言等。此外JNI提供的某些API还允许你把JVM嵌入到本地应用程序中。下图表达了JNI所扮演的角色。
本文将通过一个实例来阐述使用VC++6.0来实现JNI的完整过程。使用JNI来整合本地代码和Java代码的步骤是确定的,没有再创作的余地,所以读者可以通过本文的步骤来逐步认识到,其实Java也是"没有什么不可以"的。
一、JNI的实现
任务描述:在Java中调用windows下的消息框函数,并且从Java中传递一个字符串作为MessageBox函数的显示文本参数,显示在消息框的中间。下面让我们一起进入这一奇妙的旅程。
Step 1:写一个Java类,在这个类中包含了需要调用的本地方法的描述。
//WinMsgBox.java
package edu.netcom.jni;
public class WinMsgBox
{
static{
System.loadLibrary("WinMsgDll"); // (1)
}
public native void showMsgBox(String str); // (2)
}
|
(1)中WinMsgDll是动态链接文件的文件名,不用加扩展名,因为在不同的平台下动态链接文件扩展名是不同的,由JVM自动识别,比如在Solaris下,会被转换为WinMsgDll.so;而Win32环境下会转换为WinMsgDll.dll。这个文件名必须和Step 4中生成的文件名一致。这个文件的存放位置也很重要,它只能被放在JVM属性值java.library.path中指定的文件夹中。这个属性值可以使用System.getProperty("java.library.path");来查看。一般情况下,至少放在这几个位置是确定可靠的,windows安装目录下的system32下面,JDK安装目录下的bin下面,以及调用主类文件的当前目录。
(2)中指明了你必须用本地代码实现的方法。
Step 2:提示符下使用命令javac -d . WinMsgBox.java编译Step 1编写的java文件。
此时会在当前目录下建立一个edu\netcom\jni目录结构,并且一个WinMsgBox.class文件存在其中。
Step 3:提示符下使用命令javah -jni edu.netcom.jni.WinMsgBox,此时会在当前目录下产生一个edu_netcom_jni_WinMsgBox.h文件,注意这个文件名是由(包名+类名)组成,中间用(_)隔开。此文件内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h> // (1)
/* Header for class edu_netcom_jni_WinMsgBox */
#ifndef _Included_edu_netcom_jni_WinMsgBox
#define _Included_edu_netcom_jni_WinMsgBox
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: edu_netcom_jni_WinMsgBox
* Method: showMsgBox
* Signature: (Ljava/lang/String;)V // (2)
*/
JNIEXPORT void JNICALL Java_edu_netcom_jni_WinMsgBox_showMsgBox
(JNIEnv *, jobject, jstring); // (3)
#ifdef __cplusplus
}
#endif
#endif
|
(1)包含的jni.h存在于JDK安装目录下的include下面。
(2)(Ljava/lang/String;)V这是函数的标记符,当从本地方法端访问Java端的方法时,会用到这个标记符。JNI中为每种数据类型也定义了标记符,标记符的规则请查看JNI标准文档。
(3)在WinMsgBox.java中本地方法void showMsgBox(String str);的定义,被映射为JNIEXPORT void JNICALL Java_edu_netcom_jni_WinMsgBox_showMsgBox(JNIEnv *, jobject, jstring); 其中函数名的映射规则是(Java_包名_类名_方法名),如果存在重载的方法,则在后面还会增加每个参数的标记符。每一个方法映射到本地C函数后都会增加两个参数:JNIEnv *和jobject,关于这两个参数的用法将在后面阐述。另外,所有Java中的数据类型都会按一定规则进行映射为本地数据类型,这些数据类型都是在jni.h中定义的。下面分别按照基本数据类型,和对象类型列出。
表1 Java基本类型到本地类型的映射
表2 Java中的类到本地类型的映射
Step 4:使用VC来编写本地方法的实现函数,最后编译成.dll文件。过程如下:
1) 选择new->projects(选择Win32 Dynamic-Link Library,以Step 1中指定的库名WinMsgDll作为工程名)->OK->An ampty DLL project->Finish。
2) 选择Tools->Options->Directories(添加目录D:\J2SDK1.4.2_03\INCLUDE和D:\J2SDK1.4.2_03\INCLUDE\WIN32)。在这些目录中包含JNI所需的头文件。
3) 将Step 3生成的edu_netcom_jni_WinMsgBox.h拷贝到WinMsgDll工程文件夹中。然后FileView中添加这个头文件。
4) 添加源文件WinMsgDll.cpp,内容如下:
#include "windows.h"
#include "edu_netcom_jni_WinMsgBox.h"
/*
* Class: edu_netcom_jni_WinMsgBox
* Method: showMsgBox
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_edu_netcom_jni_WinMsgBox_showMsgBox
(JNIEnv * env, jobject obj, jstring str){
const char *msg;
msg = env->GetStringUTFChars(str,0);
MessageBox(NULL,msg,"Java invoke",MB_OK);
env->ReleaseStringUTFChars(str,msg);
}
|
5) 编译生成WinMsgBox.dll文件。并将这个.dll文件拷贝到Step 1中说明的目录中。
注意:
1) 我们知道dll文件有两种指明导出函数的方法,一种是在.def文件中定义,另一种是在定义函数时使用关键字__declspec(dllexport)。而在JNI中函数定义中的关键字JNIEXPORT实际在jni_md.h中如下定义,#define JNIEXPORT __declspec(dllexport),可见JNI默认的导出函数使用第二种。使用第二种方式产生的导出函数名会根据编译器发生变化,在有的情况下会发生找不到导出函数的问题(我们在JSP中使用JNI时就发生了这种问题,百思不得其解,后来强行加入一个.def文件就解决了)。因此最好是使用第一种方法自己定义一个.def文件来指明导出函数,这种情况下会强制使用第一种方式产生导出函数。本例中可以加入一个WinMsgDll.def文件,内容如下:
LIBRARY "WinMsgDll"
DESCRIPTION 'message Windows Dynamic Link Library'
EXPORTS
; Explicit exports can go here
Java_edu_netcom_jni_WinMsgBox_showMsgBox
|
2) 从本例中,我们可以看到WinMsgBox.java决定了edu_netcom_jni_WinMsgBox.h,而后者又决定了WinMsgDll.dll,也就是说,这是一个"牵一发而动全身"的过程,如果你改动了WinMsgBox.java,就一定要把整个步骤都走一遍(这一点一定要切记,因为这也是我们跌得鼻青脸肿后才得出的警世良言)。
3) 生成的.dll文件一定要正确拷贝到Step 1说明的目录中,本例中是将生成的WinMsgDll.dll和Step 5中的测试文件放在同一个目录下的(这也是我们困惑了很久才解决的问题)。
Step 5:编写一个测试文件来测试对WinMsgDll.dll的调用。测试文件TestJNI.java内容如下:
//TestJNI.java
import edu.netcom.jni.WinMsgBox;
public class TestJNI
{
public static void main(String[] args)
{
WinMsgBox box = new WinMsgBox();
box.showMsgBox("Wonderful!!");
}
}
|
编译,运行,windows下的对话框跃然屏幕中间。到此为此,整个JNI的实现过程就已经完成了。
二、补充说明
JNI为程序员提供了一种方法,使得他们能够充分利用JVM以外的,完全由平台决定的功能。但是你不应该滥用JNI。大多数情况可能是因为这样而必须使用JNI,如你已经有做好了的本地.dll文件,其中已经包含了大量呕心沥血的函数,而你不愿意,也不可能完全用Java重写它们,那么此时你应该用JNI。但是你必须按前面的步骤来进行,只是在Step 4中编写的.dll要调用已有的本地.dll,也就是说用Step 4中的.dll"封装"已有的.dll。
在Java中定义的本地方法映射到本地C函数后都会增加两个参数:JNIEnv *和jobject。第一个参数JNIEnv是一个指针,指向在jni.h中定义的一个数据结构,结构中包含了一系列的函数的指针,我们把它们称之为JNI函数。使用这些JNI函数可以使程序员从本地函数这一侧完成对Java的操作,如访问Java字符串、数组、调用Java的方法、成员变量、甚至处理Java端的异常等。第二个参数会根据Java类中本地方法的定义不同而不同,如果是定义为static方法,类型会是jclass,表示对特定Class对象的引用,如果是非static方法,类型是jobject,表示当前对象的引用(本例中就是对WinMsgBox对象的引用),相当于this。
C函数通过JNI接受来自Java的传参也是按基本类型直接传值,对象传引用。对于基本类型可以按照表1来使用,其它类型都必须使用JNI函数进行转换后才能在C函数中使用。如本例中从Java中传递了一个字符串,映射为JNI类型jstring。使用JNI函数GetStringUTFChars将jstring转换为UTF-8字符串,然后便可以使用C语言中的任何字符串操作函数进行操作。由于JVM在调用本地方法时,是在虚拟机中开辟了一块本地方法栈供本地方法使用,当本地方法使用完UTF-8串后,必须使用ReleaseStringUTFChars,通过它来通知虚拟机去回收UTF-8串占用的内存,否则将会造成内存泄漏,最终导致系统崩溃。同样的,如果需要将C函数返回的返回值能够正确通过JNI传给Java,也要使用JNI函数转换为前面两个表中的类型。
关于 JNI 函数的权威文档请参阅 Java Native Interface 1.5 Specification
关于 JNI 函数的教程请参阅 Java Native Interface Tutorial
分享到:
相关推荐
总结,从VC++6.0环境下的《六爻排盘》到安卓应用的转化是一项技术挑战,但通过深入理解六爻占卜原理、熟悉VC++6.0开发环境、并掌握安卓应用开发,开发者可以实现这一转变,让更多人能够随时随地体验六爻的魅力。
#### 例解VC++ 6.0 实现JNI 1. **开发环境**:介绍如何在VC++ 6.0环境中配置JNI开发环境。 2. **实例代码**:提供具体的代码示例,演示如何在VC++ 6.0中实现JNI。 3. **注意事项**:列出在使用VC++ 6.0开发JNI时...
是Visual C++ 6.0程序设计学与用教程(起跑线)的源码.本书包括内容 Java语言是目前最为流行和通用的网络编程设计语言,在Internet上有着重要而广泛的应用。本书在第一版的基础上结合新版的JDK1.4对所有例程重新...
用c语言写了一个清屏的函数,然后生成动态链接库,再通过jni在java里...动态链接库是使用VC++6.0生成的。 具体JNI的基本使用方法可以参考这个博客 http://www.cnblogs.com/mandroid/archive/2011/06/15/2081093.html
文档中提到的“解用VC++6.0实现JNI.doc”和“JNI实例教程.wps”可能包含了具体的步骤和示例,如如何在Visual C++ 6.0环境下编写和编译JNI代码,以及实际的案例分析。 通过这篇教程,开发者可以了解到如何结合Java...
java jni 传递汉字参数,包括java传入dll 从dll传出到java 包括全部 java 代码 工程文件 全部vc++6.0 Dll 代码 工程文件 找了好久资料才完成的,传上来省的大家再走弯路。
java jni 传递汉字参数,包括java传入dll 从dll传出到java 读写ini 包括全部 java 代码 工程文件 全部vc++6.0 Dll 代码 工程文件 找了好久资料才完成的,传上来省的大家再走弯路。
VC++ 6.0是一款经典的Windows平台上的C++开发工具,尽管现在已经有些过时,但对于初学者来说,它提供的环境简单易用,能够快速理解本地代码的编译和链接过程。在C++代码中,你需要按照JNI规范定义函数,使用`...
打开VC++6.0(或其他C++编译器),创建一个新的动态链接库(DLL)项目,命名为`hello`。项目创建后,需要添加必要的源文件和头文件。 2. **添加并编辑源文件** 在项目中添加`hello.cpp`文件,将通过`javah`命令...
4. **创建DLL**:在C或C++环境中(如VC++ 6.0),创建一个动态链接库(DLL)。这里,我们需要创建一个Win32 Dynamic-Link Library项目,并将`NativeHello.h`头文件及必要的JNI头文件(如`jni.h`)包含进来。然后,...
这里使用的是Java 1.6.0_03-b05版本和VC++ 6.0作为C++编译器。需要确保系统环境变量配置正确,包括指向Java的include、lib目录,以及C++的相关路径。可以通过批处理文件设置这些环境变量,以便在命令行模式下编译和...
2. **创建C++ DLL**:在VC++6.0环境下,你可以创建一个新的Win32 Dynamic-Link Library项目。定义你需要暴露给Java的函数,并遵循JNI的规范来编写函数签名。例如,一个简单的函数可能是: ```cpp #include "jni.h" ...
- C++版本:VC++6.0 确保正确设置了环境变量,包括Java的include和lib路径。这些设置对于C++调用Java尤其重要。 #### 示例:Java调用C++ 1. **定义Java类**:在Java类中声明native方法并加载C++库。例如,在`...
这通常涉及到环境变量的设置,确保包含Java的include和lib路径,以及特定于开发环境(如VC++ 6.0)的配置。此外,还需运行特定的批处理文件(如java-env.Bat),以准备必要的环境供C++调用Java。 接下来,通过Java...
6. **开发工具**:在Java和C/C++开发中,熟练使用Eclipse、MyEclipse、SVN、VC++6.0、VS2010等工具。 7. **语言能力**:流利的普通话和粤语,英语四级,能进行专业文献的阅读和口语交流。 【工作经历】部分,求职者...
对于Java部分,可能涉及到使用JavaFX或Swing库来创建用户界面,与VC++的ActiveMovie控件进行交互,或者使用JNI(Java Native Interface)来调用VC++编写的底层库。Java端可以处理跨平台兼容性问题,而VC++则专注于...
【MSComm 控件】是微软提供的一种用于串行通信的组件,主要应用于VC++、VB、Delphi等编程环境中。该控件使得开发者能够轻松处理串行端口(如COM1、COM2等)的数据传输,广泛应用于工业控制、设备通信等领域。在C#和...
26.zip Sequentially Renumber Resource ID's 重新对资源标识符编号(5KB)<END><br>27,27.zip A secondary clipboard 实现第二个剪贴板, 不过它用的热键是Ctrl+Shift+C, Ctrl+Shift+V和Ctrl+Shift+X(4KB)...