Java调用C/C++编写的第三方dll动态链接库(zz)

news/2024/7/7 23:12:33

这里主要用的方法是JNI。在网上查资料时看到很多人说用JNI非常的复杂,不仅要看很多的文档,而且要非常熟悉C/C++编程。恐怕有很多人在看到诸如此类的评论时已经决定绕道用其他方法了。本文将做详细的介绍。

AD:51CTO网+ 首届中国APP创新评选大赛火热招募中……

最近在用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的工作模式

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

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

  1. public class APCluster {   
  2.     public native int[] CallAPClusterDll( int         arg_Int,   
  3.                                           double[]    arg_DoubleArray,   
  4.                                           boolean     arg_boolean);  
  5.     static 
  6.     {  
  7.         System.loadLibrary("APClusterDllMedium");  
  8.     }  

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

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

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

2)编译生成.h文件

第一步:

javac APCluster.java 生成APCluster.class

第二步:

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

  1. /* DO NOT EDIT THIS FILE - it is machine generated */ 
  2. #include <jni.h>  
  3. /* Header for class APCluster */ 
  4. #ifndef _Included_APCluster  
  5. #define _Included_APCluster  
  6. #ifdef __cplusplus  
  7. extern "C" {  
  8. #endif10 /*  
  9.  * Class:     APCluster  
  10.  * Method:    CallAPClusterDll  
  11.  * Signature: (I[DZ)[I  
  12.  */ 
  13. JNIEXPORT jintArray JNICALL Java_APCluster_CallAPClusterDll  
  14.   (JNIEnv *, jobject, jint, jdoubleArray, jboolean);  
  15. #ifdef __cplusplus  
  16. }  
  17. #endif21
  18. #endif 

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

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

创建一个C/C++工程,工程名为APClusterDllMedium(其实,生成的dll名为APClusterDllMedium即可),导入APCluster.h这个头文件,并创建一个CPP文件,实现.h文件中的方法。

 
图2 新建工程结构

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

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

 
图3 修改工程属性

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

  1. #include "APCluster.h"   
  2. #include <stdio.h>   
  3. #include <windows.h>  
  4. #ifdef __cplusplus   
  5. extern "C" {  
  6. #endif  
  7. typedef int*  (__stdcall *APCLUSTER32)(double*, unsigned int, bool);  
  8. JNIEXPORT jintArray JNICALL Java_APCluster_CallAPClusterDll  
  9.   (JNIEnv *env, jobject _obj, jint _arg_int, jdoubleArray _arg_doublearray, jboolean _arg_boolean)  
  10. {  
  11.     HMODULE dlh = NULL;  
  12.     APCLUSTER32 apcluster32;  
  13.     if (!(dlh=LoadLibrary("apclusterwin.dll")))        //第三方DLL位置  
  14.     {  
  15.         printf("LoadLibrary() failed: %d\n", GetLastError());   
  16.     }  
  17.     if (!(apcluster32 = (APCLUSTER32)GetProcAddress(dlh, "apcluster32")))    //具体调用apcluster32方法  
  18.     {  
  19.         printf("GetProcAddress() failed: %d\n", GetLastError());   
  20.     }  
  21.     int        m_int = _arg_int;  //类型转换  
  22.     double*    m_doublearray = env->GetDoubleArrayElements(_arg_doublearray, NULL);  
  23.     bool       m_boolean = _arg_boolean;  
  24.     int* ret = (*apcluster32)(m_doublearray, m_int, m_boolean); /* actual function call */ 
  25.     jintArray result = env->NewIntArray(_arg_int);  
  26.     env->SetIntArrayRegion(result, 0, _arg_int, (const jint*)ret);  
  27.     FreeLibrary(dlh); /* unload DLL and free memory */ 
  28.     if(ret)   
  29.     {  
  30.          free(ret);   
  31.     }  
  32.     return result;  
  33. }  
  34. #ifdef __cplusplus  
  35. }  
  36. #endif 

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

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

 
图4 添加JNI目录

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

JNIEXPORT jintArray JNICALL Java_APCluster_CallAPClusterDll
(JNIEnv *env, jobject _obj, jint _arg_int, jdoubleArray _arg_doublearray, jboolean _arg_boolean) ......

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

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

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

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,生成相应的APCluster.dll文件,将这个dll放到java工程目录下。

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

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

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

  1. public class Test    
  2. {  
  3.     public static void main(String[] args)   
  4.     {   
  5.         double     arg_doublearray[] = {0.10.20.3};   
  6.         int        arg_int = 3;   
  7.         boolean    arg_boolean = true;   
  8.         int[]  result = new APCluster().CallAPClusterDll(arg_int, arg_doublearray, arg_boolean);  
  9.         .....  
  10.     }  

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

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

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

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

原文链接:http://www.cnblogs.com/AnnieKim/archive/2012/01/01/2309567.html


来源: http://developer.51cto.com/art/201201/311363.htm


来自为知笔记(Wiz)



http://lihuaxi.xjx100.cn/news/245182.html

相关文章

一张图带你了解 Spring Cloud 微服务架构!

点击上方“搜云库技术团队”&#xff0c;选择“设为星标”回复“1024”或“面试题”获取4T学习资料FeignEurekaRibbonHystrixZuulConfigZipkin其它Spring cloud 作为当下主流的微服务框架&#xff0c;让我们实现微服务架构简单快捷Spring cloud中各个组件在微服务架构中扮演的角…

nutch如何发布插件

为什么80%的码农都做不了架构师&#xff1f;>>> 1.修改插件&#xff0c;在原有的插件上修改&#xff0c;比如parse-html插件上修改。 2.修改插件之后&#xff0c;把第三方的包放到/nutch/runtime/local/lib下&#xff08;经测试&#xff0c;只有在此目录下&#xf…

第二十章:异步和文件I/O.(十三)

通过该开销&#xff0c;可以开始实际编写应用程序。 TextFileAsyncPage的XAML文件与TextFileTryoutPage相同&#xff0c;但必须将代码隐藏文件设置为使用异步文件I / O方法。 必须在此处捕获文件I / O函数中可能发生的任何异常&#xff0c;这意味着任何可以抛出异常的方法必须与…

LeetCode - 34. Search for a Range

34. Search for a Range Problems Link ---------------------------------------------------------------------------- Mean: 给定一个有序数组和一个数k&#xff0c;求k在这个数组中的起始下标和结束下标. analyse: 二分查找. Time complexity: O(N) view code /***…

开发三年,如何摆脱日复一日的CRUD?

IT行业技术人员的薪资向来不低&#xff0c;吸引了一批又一批明知“苦逼”又前赴后继的毕业生。作为一名技术从业者&#xff0c;本应该崇拜技术&#xff0c;在商业项目中不断在纵向领域中做深度打磨。但在绝大多数中小型的公司中&#xff0c;业务线要求我们快速迭代&#xff0c;…

win7安装mysql-8.0.13-winx64

这里展示一下&#xff0c;由于需要安装一个版本测试一下数据&#xff0c;其实就是超简单的啦。 下包 注:https://dev.mysql.com/downloads/mysql/ 解压与配置 [mysqld] basedirC:\\Users\\hp\\Downloads\\mysql-8.0.13-winx64 datadirC:\\Users\\hp\\Downloads\\mysql-8.0.13-w…

putty或xshell上用vi/vim小键盘无法使用的解决方法

在putty或xshell上用vi/vim的时候&#xff0c;开NumLock时按小键盘上的数字键并不能输入数字&#xff0c;而是出现一个字母然后换行(实际上是命令模式上对应上下左右的键)。解决方法&#xff1a;putty&#xff1a;选项Terminal->Features里&#xff0c;找到Disable applicat…

android -各种适配器

2019独角兽企业重金招聘Python工程师标准>>> 参考文章:http://blog.csdn.net/a_large_swan/article/details/7535337 数据源不同而已 1. String[]: ArrayAdapter 2. List<Map<String,?>>: SimpleAdapter 3. 数据库Cursor: SimpleCursorAdapter 第一种…