Java用JNI实现对用VC++编写的动态库的调用

来源:互联网 发布:拼豆图纸软件 编辑:程序博客网 时间:2024/05/29 15:39

JavaJNI实现对用VC++编写的动态库的调用

在很多的情况下,我们已经有了现成的供调用的动态库,而不需要用JAVA来再次实现一次。JAVA能不能实现对由其他语言编写的程序呢?答案是肯定的。JAVA内部提供了JNI即为JAVA提供了一个本地的代码接口,这样,我们就可以调用由其他语言实现的代码了。

空洞的说理没什么说服力,现在用一个JAVA调用VC++动态库的例子来说明一下整个过程。

举例如下:

Step1

首先用VC++创建一个空的动态库工程,具体的实现如下:

<!--[if !supportLists]-->(1)    <!--[endif]-->jisuandong.h文件代码如下:

//这部分是固定的预定义,不用修改

#ifdef_cplusplus

#define EXPORT extern “C” _declspec(dllexport)

#else

#define EXPORT _declspec(dllexport)

#endif

//.cpp中的调用函数说明

EXPORT double CALLBACK jisuan(int i,int j);

2.def文件,代码如下:

LIBRARY “jisuandong”

DESCRIPTION ‘mydlltext’

EXPORTS

Jisuan

3.cpp文件,代码如下:

#inlcude “stdafx.h”

#include “jisuandong.h”

#include “math.h”

//函数实现

EXPORT double CALLBACK jisuan(int i,int j)

{

 return sqre(i*i+j*j);

}

编译生成.dll文件就行,这个动态库,是比如是你要供JAVA调用的以前用VC编写的已经实现的动态库。

Step2

编写JAVA程序,这里的JAVA程序是用来生成.h文件的,以备生成调用上述DLL的动态库,最后供JAVA应用程序来调用。这里只是一个示例,所以生成.h文件的JAVA程序和调用的JAVA应用程序没有分开,一般情况是分开写的。JAVA程序代码如下:

1

class NativeTest

{

 public static void main(String [] args)

 {

  NativeTest aTest=new NativeTest();

  int i0=Integer.valueOf(args[0]).intValue();

  int i1=Integer.valueOf(args[1]).intValue();

  double d=aTest.nativeHypotenuse(i0,i1);

  System.out.println("the value for the Hypotenuse for a triangle of sides "+args[0]+

  " and "+args[1]+ " is "+d);

 }

 public native double nativeHypotenuse(int i,int j);

// static

// {

//  System.loadLibrary("NativeTest");

// }

 public NativeTest()

 {

  System.loadLibrary("NativeTest");//这里是最后封装的DLLJAVA应用程序调用

 }

}

<!--[if !supportLists]-->(2)    <!--[endif]-->javac NativeTest.java编译生成CLASS文件。

<!--[if !supportLists]-->(3)    <!--[endif]-->javah –jni NativeTest生成.h文件

Step3

VC再创建一个空动态库工程,用来调用上述动态库,此动态库就是在你原来打算调用的DLL上的另一层封装,才能实现JAVA应用程序对它的调用。

<!--[if !supportLists]-->(1)    <!--[endif]-->NativeTest.h文件也就是上述生成的.h文件

/* DO NOT EDIT THIS FILE - it is machine generated */

#include <jni.h>

/* Header for class NativeTest */

 

#ifndef _Included_NativeTest

#define _Included_NativeTest

#ifdef __cplusplus

extern "C" {

#endif

/*

 * Class:     NativeTest

 * Method:    nativeHypotenuse

 * Signature: (II)D

 */

JNIEXPORT jdouble JNICALL Java_NativeTest_nativeHypotenuse

  (JNIEnv *, jobject, jint, jint);

 

#ifdef __cplusplus

}

#endif

#endif

2.def文件

LIBRARY NativeTest

DESCRIPTION mydll test

EXPORTS

JNIEXPORT jdouble JNICALL Java_NativeTest_nativeHypotenuse

 

3.cpp文件

#include “stdafx.h”

#include <math.h>

#include “NativeTest.h”

 

#include “jisuandong.h”//因为要调用jisuandong.dll

#pragma comment(lib,”jisuandong.lib”);//因为这里是静态调用,所以只需把上述lib文件拷贝到本目录下

//函数实现

JNIEXPORT jdouble JNICALL Java_NativeTest_nativeHypotenuse

  (JNIEnv *env, jobject obj, jint i, jint j)

{

 double d;

 return d = jisuan(i,j);

}

(4)最后把jisuandong.dllNativeTest.dll拷贝到JAVA应用程序的目录下,调用即可。

其他补充如下:

JNI为程序员提供了一种方法,使得他们能够充分利用JVM以外的,完全由平台决定的功能。但是你不应该滥用JNI。大多数情况可能是因为这样而必 须使用JNI,如你已经有做好了的本地.dll文件,其中已经包含了大量呕心沥血的函数,而你不愿意,也不可能完全用Java重写它们,那么此时你应该用 JNI

Java中定义的本地方法映射到本地C函数后都会增加两个参数:JNIEnv *jobject。第一个参数JNIEnv是一个指针,指向在jni.h中定义的一个数据结构,结构中包含了一系列的函数的指针,我们把它们称之为 JNI函数。使用这些JNI函数可以使程序员从本地函数这一侧完成对Java的操作,如访问Java字符串、数组、调用Java的方法、成员变量、甚至处 理Java端的异常等。第二个参数会根据Java类中本地方法的定义不同而不同,如果是定义为static方法,类型会是jclass,表示对特定 Class对象的引用,如果是非static方法,类型是jobject,表示当前对象的引用,相当于 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函数转换为 前面两个表中的类型。

1 Java基本类型到本地类型的映射

 

<!--[if !vml]--><!--[endif]-->

<!--[if !vml]--><!--[endif]-->

2 Java中的类到本地类型的映射

 

<!--[if !vml]--><!--[endif]-->

 

<!--[if !vml]--><!--[endif]-->

呵呵,写到这里有点累了,希望对大家有用,本人也是由于工作需要才学的JAVA,希望大家多多交流!!!^_^

 

原创粉丝点击