`
samwong
  • 浏览: 281090 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Java调用C/C++编写的第三方dll动态链接库(非native API)--- JNI

 
阅读更多

注:2013年6月6日,我对该博文进行了修改,增加了源代码以及更直观详细的讲解。如果需要代码,可以到文章最后给出的链接进行下载。

最近在用weka做一个数据挖掘相关的项目,不得不说,weka还是一个不错的开放源代码库,提供了很多最常用的分类和聚类算法。

在我的项目中要用到一个聚类算法,Affinity Propagation(AP),由多伦多大学的Brendan J. Frey发表于2007年。相比其他的聚类算法,AP算法的聚类结果更加准确。

在AP的官方网站公布了AP算法的动态链接库,我的目标就是实现在Java工程中调用这个动态链接库。

在网上查了资料,发现,如果仅仅是想调用Windows的Native API还是比较省事的,这里我主要针对第三方dll的调用。

下面进入正题。

这里主要用的方法是JNI。在网上查资料时看到很多人说用JNI非常的复杂,不仅要看很多的文档,而且要非常熟悉C/C++编程。恐怕有很多人在看到诸如此类的评论时已经决定绕道用其他方法了。但是,假如你要实现的功能并不复杂(简单的参数传递,获取返回值等等),我还是支持使用这个方法的。

Java Native Interface,简称JNI,是Java平台的一部分,可用于让Java和其他语言编写的代码进行交互。下面是从网上摘取的JNI工作示意图。

图1 JNI的工作模式

下面就举具体的例子说明一下使用步骤

先说明一下我们要达到的目的

假设我们现在有一个第三方dll叫做ThirdPartyDll.dll,我们想调用其中的realfunction方法,该方法的接口在ThirdParty.h中给出,如下:

复制代码
 1 // The ThirdPartyDll.dll contains the following function.
 2 // Name: 
 3 //   realfunction
 4 // Function:
 5 //   Turn a double type array to an int type array.
 6 // Parameters:
 7 //   nDoubleArray: a double type array needed to cutoff.
 8 //   nSize: the size of nDoubleArray.
 9 //   nPrint: print the original array if nPrint == true.
10 // Return Value:
11 //   This function will allocate a new int type array inside for storing the result.
12 //   So, DO NOT forget to release the space after using the result! 
13 //   (Of course, we'd better not to design a function like this when coding:))
14 // ----------------------------------------------------------------------------------
15 
16 int* realfunction(double* nDoubleArray, unsigned int nSize, bool nPrint);
复制代码

我们现在要尝试根据这个dll和这个函数接口,在java程序中通过一个中介dll调用该函数。

1) 编写一个类,声明native方法

复制代码
 1 public class CallThirdParty {
 2       
 3       public native int[] CallThirdPartyDll(double[]    arg_DoubleArray,
 4                                             int         arg_SizeofArray,
 5                                             boolean     arg_print);
 6       static
 7       {
 8           System.loadLibrary("MediumDll");
 9       }
10 }
复制代码

上面是CallThirdParty.java文件,定义了一个CallThirdParty类,其中有一个方法CallThirdPartyDll(),需要传递三种不同类型的参数,并且返回一个整型数组。

注意,这里只需要声明这个方法,并不需要实现,具体实现就在MediumDll中。

MediumDll就像中介一样,Java通过调用这个中介Dll中的CallThirdPartyDll方法,间接调用真正的第三方Dll。

2)编译生成.h文件

cmd到CallThirdParty.java目录下,执行以下几个命令,生成.h文件。(需要设定java环境变量)

第一步:

javac CallThirdParty.java生成CallThirdParty.class

第二步:

javah CallThirdParty生成CallThirdParty.h头文件,内容如下:


复制代码
 1 /* DO NOT EDIT THIS FILE - it is machine generated */
 2 #include <jni.h>
 3 /* Header for class CallThirdParty */
 4 
 5 #ifndef _Included_CallThirdParty
 6 #define _Included_CallThirdParty
 7 #ifdef __cplusplus
 8 extern "C" {
 9 #endif
10 /*
11  * Class:     CallThirdParty
12  * Method:    CallThirdPartyDll
13  * Signature: ([DIZ)[I
14  */
15 JNIEXPORT jintArray JNICALL Java_CallThirdParty_CallThirdPartyDll
16   (JNIEnv *, jobject, jdoubleArray, jint, jboolean);
17 
18 #ifdef __cplusplus
19 }
20 #endif
21 #endif
复制代码

注意,CallThirdParty.h这个头文件的内容是不能修改的,否则JNI会找不到相对应的CallThirdPartyDll()的实现。

3)创建C/C++工程,实现CallThirdPartyDll()方法。

创建一个C/C++工程,工程名为MediumDll(其实,生成的dll名为MediumDll即可),导入上一步生成的CallThirdParty.h这个头文件以及官方给出的接口文件ThirdParty.h,并创建一个CPP文件,实现CallThirdParty.h文件中的方法。

图2 新建工程结构

由于我默认创建的工程是win32控制台程序并且最后生成的是.exe文件,所以还要做一步工程属性修改,让它生成.dll后缀文件。

打开Project Property ->General,做以下修改:

图3 修改工程属性

下面就是实现JNIEXPORT jintArray JNICALL Java_CallThirdParty_CallThirdPartyDll(JNIEnv *, jobject, jdoubleArray, jint, jboolean);这个方法了。先贴代码再慢慢解释吧。

复制代码
 1 #include "CallThirdParty.h"
 2 #include "ThirdParty.h"
 3 #include <iostream>
 4 #include <Windows.h>
 5 using namespace std;
 6 
 7 #ifdef __cplusplus
 8 extern "C" {
 9 #endif
10 
11 typedef int* (*ThirdPartyFunc)(double*, unsigned int, bool);
12 
13 JNIEXPORT jintArray JNICALL Java_CallThirdParty_CallThirdPartyDll (JNIEnv *env, jobject _obj, jdoubleArray _arg_doublearray, jint _arg_int, jboolean _arg_boolean)
14 {
15     HMODULE dlh = NULL;
16     ThirdPartyFunc thirdPartyFunc;
17 
18     if (!(dlh=LoadLibrary("ThirdPartyDll.dll")))      
19     {
20     printf("LoadLibrary() failed: %d\n", GetLastError()); 
21     }
22     if (!(thirdPartyFunc = (ThirdPartyFunc)GetProcAddress(dlh, "realfunction")))  
23     {
24         printf("GetProcAddress() failed: %d\n", GetLastError()); 
25     }
26 
27     int        m_int = _arg_int;  
28     double*    m_doublearray = env->GetDoubleArrayElements(_arg_doublearray, NULL);
29     bool       m_boolean = _arg_boolean;
30 
31     int* ret = (*thirdPartyFunc)(m_doublearray, m_int, m_boolean); /* actual function call */
32     
33     jintArray result = NULL;
34     if (ret)
35     {
36         result = env->NewIntArray(_arg_int);
37         env->SetIntArrayRegion(result, 0, _arg_int, (const jint*)ret);
38     }
39 
40     FreeLibrary(dlh); /* unload DLL and free memory */
41     if(ret) 
42     {
43         free(ret); 
44     }
45 
46     return result;
47 }
48 
49 #ifdef __cplusplus
50 }
51 #endif
复制代码

a)首先为了#include <jni.h>,必须添加JNI所在的目录。

打开Project Property -> C/C++ -> General -> Additional Include Directories添加相应目录:

图4 添加JNI目录

b)在CallThirdParty.h文件中自动生成的函数,只标识了函数参数类型,为了引用这些参数,自己起一个相应的名字:

JNIEXPORT jintArray JNICALL Java_CallThirdParty_CallThirdPartyDll
(JNIEnv *env, jobject_obj, jint_arg_int, jdoubleArray_arg_doublearray, jboolean_arg_boolean) ......

c)声明函数指针,就是你要调用的第三方dll中函数的类型。

d)LoadLibrary,导入真正的第三方Dll,并找到要调用的方法的函数地址

把这个函数地址赋值给函数指针,接下来就可以通过这个函数指针调用真正的realfunction函数了!

e)类型转换:

读读jni.h文件就知道jdouble和double其实是一个东西,jboolean就是unsigned char类型,jni.h中是这么声明的:

1 typedef unsigned char   jboolean;
2 typedef unsigned short  jchar;
3 typedef short           jshort;
4 typedef float           jfloat;
5 typedef double          jdouble;

但是数组类型就没有这么简单,获取数组要使用类型相对应的env->GetTypeArrayElement(jTypeArray...)。

最后,要返回一个jint类型的数组,就要新创建一个此类型的数组,再为其赋值:

1 jintArray result = env->NewIntArray(_arg_int);
2 env->SetIntArrayRegion(result, 0, _arg_int, (const jint*)ret);

其中,_arg_int代表的是创建数组的长度。

最后return result。

4)Build这个工程。

Build,生成相应的MediumDll.dll文件,并将其与真正要调用的第三方动态链接库ThirdPartyDll.dll放到java工程目录下。

图5 将生成的dll放到java工程下

5)编写测试java程序,调用dll库。

以下为测试程序,Test.java:

复制代码
 1 public class Test {
 2      public static void main(String[] args) {
 3          
 4          double doubleArray[] = {1.1, 2.5, 5,2};
 5          
 6          CallThirdParty callThirdParty = new CallThirdParty();
 7          int cutOffArray[] = callThirdParty.CallThirdPartyDll(doubleArray, 3, true);
 8          
 9          for (int i = 0; i < cutOffArray.length; ++i)
10              System.out.println(cutOffArray[i]);
11      }
12 }
复制代码

运行,控制台输出结果:

Value of 0: 1.1
Value of 1: 2.5
Value of 2: 5
1
2
5

到此,java调用第三方dll就基本完成了。

本文也主要是介绍大概的操作流程,至于具体应该使用哪些API就只有去研究官方文档了。

另外还有一些需要注意的问题,比如64位的程序去调用32位的dll会报错啊等等...这些都是细节问题了。

最后,个人认为,自己动手实践还是很重要,网上都说这个复杂那个难,但是至于难还是不难,还是要实践了才知道...不能不去尝试...

-----------------------------------------------

最后的最后,附上相关代码的链接:https://github.com/AnnieKim/ForMyBlog/tree/master/20120101

其中,projects文件夹下是工程文件,在eclipse和vs2008下进行,如果可以直接运行那就最好了。

另外,source文件夹下是所有的相关代码,包括第三方dll和生成的Mediumdll。

如果遇到什么问题,欢迎讨论。

(完)

分享到:
评论

相关推荐

    JNI学习示例代码,含java代码工程和win32 dll工程

    代码被编写成汇编程序或者C/C++程序,并组装为动态库。也就允许非静态绑定用法。这提供了一个在Java平台上调用C/C++的一种途径。 ———————————————— 版权声明:本文为CSDN博主「clever101」的原创...

    Java调用C++类库--JNI

    代码示例,java如何调用c++ 注意:将NativeCode.dll拷贝到C:\Program Files\Java\jdk1.6.0_25\bin目录下。

    JAVA调用C++的dell应用例子

    Jni(Java Native Interface)是sun提供的java与系统中的原生方法交互的技术(在windows\linux系统中,实现java与native method互调)。目前只能由c/c++实现。后两个都是sourceforge上的开源项目,同时也都是基于jni...

    JNI完全技术手册 带完整书签

    Chap5:使用JNI技术实现java程序调用第三方dll(c/c++)文件的功能... 47 Chap9:如何编写jni方法(转载)... 55 1、实例一:在jni中调用标准c中自带的函数printf(): 57 2、实例二、调用c 语言用户定义的函数... 58 ...

    Java的JNA-JNI的加强版

    JNA(Java Native Access )提供一组Java工具类用于在运行期动态访问系统本地库(native library:如Window的dll)而不需要编写任何Native/JNI代码。开发人员只要在一个java接口中描述目标native library的函数与...

    JNI文档资料源码_2020_01_22

    【Android NDK 开发】Visual Studio 2019 使用 CMake 开发 JNI 动态库 ( 动态库编译配置 | JNI 头文件导入 | JNI 方法命名规范 ) 博客地址 : https://hanshuliang.blog.csdn.net/article/details/104068609 博客...

    java 调用dll

    java 调用dll In this tutorial, I will try to explain how to call native functions which are written in C or C++ from Java, using JNI.

    详解AndroidStudio JNI +Gradle3.0以上JNI爬坑之旅

    我们先将写好的C/C++代码编译成对应平台的动态库(windows是.dll文件,linux是.so文件)。 下面我们来举个栗子:使用AndroidStudio来实现JNI 3.要实现JNI先下载NDK,那么NDK又是什么呢?(面试宝典来了,赶紧掏出小...

    java开源包1

    同时,任何第三方都可以使用OAUTH认证服务,任 何服务提供商都可以实现自身的OAUTH认证服务,因而OAUTH是开放的。业界提供了OAUTH的多种实现如PHP,JavaScript,Java,Ruby等各种语言开发包,大大节约了程序员的时间...

    java开源包10

    同时,任何第三方都可以使用OAUTH认证服务,任 何服务提供商都可以实现自身的OAUTH认证服务,因而OAUTH是开放的。业界提供了OAUTH的多种实现如PHP,JavaScript,Java,Ruby等各种语言开发包,大大节约了程序员的时间...

    java开源包4

    同时,任何第三方都可以使用OAUTH认证服务,任 何服务提供商都可以实现自身的OAUTH认证服务,因而OAUTH是开放的。业界提供了OAUTH的多种实现如PHP,JavaScript,Java,Ruby等各种语言开发包,大大节约了程序员的时间...

    java开源包11

    同时,任何第三方都可以使用OAUTH认证服务,任 何服务提供商都可以实现自身的OAUTH认证服务,因而OAUTH是开放的。业界提供了OAUTH的多种实现如PHP,JavaScript,Java,Ruby等各种语言开发包,大大节约了程序员的时间...

    JAVA上百实例源码以及开源项目源代码

     Java 3DMenu 界面源码,有人说用到游戏中不错,其实平时我信编写Java应用程序时候也能用到吧,不一定非要局限于游戏吧,RES、SRC资源都有,都在压缩包内。 Java zip压缩包查看程序源码 1个目标文件 摘要:Java源码...

    java开源包6

    同时,任何第三方都可以使用OAUTH认证服务,任 何服务提供商都可以实现自身的OAUTH认证服务,因而OAUTH是开放的。业界提供了OAUTH的多种实现如PHP,JavaScript,Java,Ruby等各种语言开发包,大大节约了程序员的时间...

    java开源包9

    同时,任何第三方都可以使用OAUTH认证服务,任 何服务提供商都可以实现自身的OAUTH认证服务,因而OAUTH是开放的。业界提供了OAUTH的多种实现如PHP,JavaScript,Java,Ruby等各种语言开发包,大大节约了程序员的时间...

    java开源包101

    同时,任何第三方都可以使用OAUTH认证服务,任 何服务提供商都可以实现自身的OAUTH认证服务,因而OAUTH是开放的。业界提供了OAUTH的多种实现如PHP,JavaScript,Java,Ruby等各种语言开发包,大大节约了程序员的时间...

    java开源包5

    同时,任何第三方都可以使用OAUTH认证服务,任 何服务提供商都可以实现自身的OAUTH认证服务,因而OAUTH是开放的。业界提供了OAUTH的多种实现如PHP,JavaScript,Java,Ruby等各种语言开发包,大大节约了程序员的时间...

    java开源包8

    同时,任何第三方都可以使用OAUTH认证服务,任 何服务提供商都可以实现自身的OAUTH认证服务,因而OAUTH是开放的。业界提供了OAUTH的多种实现如PHP,JavaScript,Java,Ruby等各种语言开发包,大大节约了程序员的时间...

    JAVA上百实例源码以及开源项目

     Java 3DMenu 界面源码,有人说用到游戏中不错,其实平时我信编写Java应用程序时候也能用到吧,不一定非要局限于游戏吧,RES、SRC资源都有,都在压缩包内。 Java zip压缩包查看程序源码 1个目标文件 摘要:Java源码...

    java开源包3

    同时,任何第三方都可以使用OAUTH认证服务,任 何服务提供商都可以实现自身的OAUTH认证服务,因而OAUTH是开放的。业界提供了OAUTH的多种实现如PHP,JavaScript,Java,Ruby等各种语言开发包,大大节约了程序员的时间...

Global site tag (gtag.js) - Google Analytics