zoukankan      html  css  js  c++  java
  • Java调用C/C++编写的第三方dll动态链接库(zz)

    这里主要用的方法是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 ", GetLastError());   
    16.     }  
    17.     if (!(apcluster32 = (APCLUSTER32)GetProcAddress(dlh, "apcluster32")))    //具体调用apcluster32方法  
    18.     {  
    19.         printf("GetProcAddress() failed: %d ", 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






  • 相关阅读:
    Begin Example with Override Encoded SOAP XML Serialization
    State Machine Terminology
    How to: Specify an Alternate Element Name for an XML Stream
    How to: Publish Metadata for a WCF Service.(What is the Metadata Exchange Endpoint purpose.)
    Beginning Guide With Controlling XML Serialization Using Attributes(XmlSerializaiton of Array)
    Workflow 4.0 Hosting Extensions
    What can we do in the CacheMetaData Method of Activity
    How and Why to use the System.servicemodel.MessageParameterAttribute in WCF
    How to: Begin Sample with Serialization and Deserialization an Object
    A Test WCF Service without anything of config.
  • 原文地址:https://www.cnblogs.com/strinkbug/p/5231257.html
Copyright © 2011-2022 走看看