Java调用C/C++编写的第三方dll动态链接库(zz)
这里主要用的方法是JNI。在网上查资料时看到很多人说用JNI非常的复杂,不仅要看很多的文档,而且要非常熟悉C/C++编程。恐怕有很多人在看到诸如此类的评论时已经决定绕道用其他方法了。本文将做详细的介绍。
最近在用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方法
- public class APCluster {
- public native int[] CallAPClusterDll( int arg_Int,
- double[] arg_DoubleArray,
- boolean arg_boolean);
- static
- {
- System.loadLibrary("APClusterDllMedium");
- }
- }
上面是APCluster.java文件,定义了一个APCluster类,其中有一个方法CallAPClusterDll(),需要传递三种不同类型的参数,并且返回一个整型数组。
注意,这里只需要声明这个方法,并不需要实现,具体实现就在APClusterDllMedium中。
APClusterDllMedium就像中介一样,Java通过调用这个中介Dll中的CallAPClusterDll方法,间接调用真正的第三方Dll。
2)编译生成.h文件
第一步:
javac APCluster.java 生成APCluster.class
第二步:
javah APCluster 生成APCluster.h头文件,内容如下:
- /* DO NOT EDIT THIS FILE - it is machine generated */
- #include <jni.h>
- /* Header for class APCluster */
- #ifndef _Included_APCluster
- #define _Included_APCluster
- #ifdef __cplusplus
- extern "C" {
- #endif10 /*
- * Class: APCluster
- * Method: CallAPClusterDll
- * Signature: (I[DZ)[I
- */
- JNIEXPORT jintArray JNICALL Java_APCluster_CallAPClusterDll
- (JNIEnv *, jobject, jint, jdoubleArray, jboolean);
- #ifdef __cplusplus
- }
- #endif21
- #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); 这个方法了。先贴代码再慢慢解释吧。
- #include "APCluster.h"
- #include <stdio.h>
- #include <windows.h>
- #ifdef __cplusplus
- extern "C" {
- #endif
- typedef int* (__stdcall *APCLUSTER32)(double*, unsigned int, bool);
- JNIEXPORT jintArray JNICALL Java_APCluster_CallAPClusterDll
- (JNIEnv *env, jobject _obj, jint _arg_int, jdoubleArray _arg_doublearray, jboolean _arg_boolean)
- {
- HMODULE dlh = NULL;
- APCLUSTER32 apcluster32;
- if (!(dlh=LoadLibrary("apclusterwin.dll"))) //第三方DLL位置
- {
- printf("LoadLibrary() failed: %d\n", GetLastError());
- }
- if (!(apcluster32 = (APCLUSTER32)GetProcAddress(dlh, "apcluster32"))) //具体调用apcluster32方法
- {
- printf("GetProcAddress() failed: %d\n", GetLastError());
- }
- int m_int = _arg_int; //类型转换
- double* m_doublearray = env->GetDoubleArrayElements(_arg_doublearray, NULL);
- bool m_boolean = _arg_boolean;
- int* ret = (*apcluster32)(m_doublearray, m_int, m_boolean); /* actual function call */
- jintArray result = env->NewIntArray(_arg_int);
- env->SetIntArrayRegion(result, 0, _arg_int, (const jint*)ret);
- FreeLibrary(dlh); /* unload DLL and free memory */
- if(ret)
- {
- free(ret);
- }
- return result;
- }
- #ifdef __cplusplus
- }
- #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中是这么声明的:
- typedef unsigned char jboolean;
- typedef unsigned short jchar;
- typedef short jshort;
- typedef float jfloat;
- typedef double jdouble;
但是数组类型就没有这么简单,获取数组要使用类型相对应的env->GetTypeArrayElement(jTypeArray...)。
最后,要返回一个jint类型的数组,就要新创建一个此类型的数组,再为其赋值:
- jintArray result = env->NewIntArray(_arg_int);
- 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:
- public class Test
- {
- public static void main(String[] args)
- {
- double arg_doublearray[] = {0.1, 0.2, 0.3};
- int arg_int = 3;
- boolean arg_boolean = true;
- int[] result = new APCluster().CallAPClusterDll(arg_int, arg_doublearray, arg_boolean);
- .....
- }
- }
到此,java调用第三方dll就基本完成了。
本文也主要是介绍大概的操作流程,至于具体应该使用哪些API就只有去研究官方文档了。
另外还有一些需要注意的问题,比如64位的程序去调用32位的dll会报错啊等等...这些都是细节问题了。
最后,个人认为,自己动手实践还是很重要,网上都说这个复杂那个难,但是至于难还是不难,还是要实践了才知道...不能不去尝试...
原文链接:http://www.cnblogs.com/AnnieKim/archive/2012/01/01/2309567.html
Java调用C/C++编写的第三方dll动态链接库(zz)的更多相关文章
- 关于在VB.NET中调用使用VC++编写的类库dll的一点笔记
前言 结对作业要求一出来,我就立刻想到了把“计算核心”封装成dll,然后使用vb.net编写UI调用dll的思路.然而在实现过程中却遇到了很多的问题. 我在这个过程中是负责使用vb.net编写UI并调 ...
- 可供VC调用的QT编写的界面DLL方法
一般直接编写的QT动态库是无法被Windows下的VC6.0等调用的. 如下步骤 第一步:必须要在QT界面库源码下包含qtwinmigrate的源码包和库,网上可下载到. 第二步:在QT的proc文件 ...
- Java调用明华RF读写器DLL文件的方法
首先jdk必须得是32位的,IDE也必须是32位的(我用的idea,所以为了使用32位的,下载了2018年1月版本的). 明华RF读写器演示文件提供了一份名为mwrf32.dll的动态链接库文件 ja ...
- java调用C/C++写的dll(转)
源:java调用C/C++写的dll Java语言本身具有跨平台性,如果通过Java调用DLL的技术方便易用,使用Java开发前台界面可以更快速,也能带来跨平台性. Java调用C/C++写好的DLL ...
- java调用dll-JNA
介绍 给大家介绍一个最新的访问本机代码的 Java 框架 —JNA . JNA(Java Native Access) 框架是一个开源的 Java 框架,是 SUN 公司主导开发的,建立在经典的 JN ...
- Java调用外部程序常用算法和封装类
一个项目不可能只使用一种编程语言来开发,也不可能由一个人开发,所以,Java程序员要学会和使用其他编程语言的程序员合作.那么,让我来发布一个工具类--Java外接程序扩展包,并将相应算法发布.Java ...
- Java调用第三方dll文件的使用方法 System.load()或System.loadLibrary()
Java调用第三方dll文件的使用方法 public class OtherAdapter { static { //System.loadLibrary("Connector") ...
- [JNA系列]Java调用Delphi编写的Dll之JNA使用
介绍 给大家介绍一个最新的访问本机代码的 Java 框架 —JNA . JNA(Java Native Access) 框架是一个开源的 Java 框架,是 SUN 公司主导开发的,建立在经典的 JN ...
- Eclipse、MinGW、JNI编写C++生成dll, Java端调用的完整示例(附java.lang.UnsatisfiedLinkError解决方法)
Eclipse.MinGW.JNI编写C++生成dll, Java端调用的完整示例(附java.lang.UnsatisfiedLinkError解决方法) 问题背景:之前的JNI编程都是基于And ...
随机推荐
- ASP.NET处理301重定向方法 带示例 (demo)
System.Web.HttpContext.Current.Response.Status = "301 Moved Permanently"; System.Web.HttpC ...
- lvm[12446]: Another thread is handling an event. Waiting
在检查一Linux服务器时,发现日志里面有大量"lvm[12446]: Another thread is handling an event. Waiting..." Jul ...
- SQLServer和Oracle创建视图用户
在数据集成的开发中,经常会需要给对方创建视图,让其可以查看一些必要的数据.既在数据库中创建用户,并赋给该用户查询视图的权限 一.SQLServer --创建登录用户账户USE [master] GO ...
- IDENT_CURRENT ,@@identity,SCOPE_IDENTITY() 之间对比
获取表的标识值,有3种比较常见的用法 IDENT_CURRENT ,@@identity,SCOPE_IDENTITY(),有啥不一样呢? 3个关键字在联机手册中的解释 IDENT_CURRENT ...
- Mac上打开拷贝到移动硬盘里的文件提示“已经被osx使用不能打开”解决办法
在终端里面粘贴xattr -d com.apple.FinderInfo(此处按一下空格),然后把文件拖进终端窗口,按一下回车就好了
- mysql学习笔记(一)
my建表操作 创建表 create Table <表名> ( 字段名1,数据类型 [列级约束] [默认值], 字段名2,数据类型 [列级约束] [默认值], ... [表级约束], [co ...
- Js 关于console 在IE 下的兼容问题
程序员在开发代码的过程中,使用console作为调试代码过程的一种手段. 发布到测试生产环境,发现IE8 出现加载错误.使用开发者工具调试,发现可以绕过问题. 通过网络搜索和在项目中进行修正. 以下办 ...
- Type mismatch: cannot convert from MainFragment to Fragment 报错
源码: FragmentTransaction mFragmentTranscation = getSupportFragmentManager().beginTransaction(); Fragm ...
- Mac SVN 命令行
Mac自带了SVN命令行,如我的升级到10.10(OSX yosemite)后命令行版本为1.7.10 以下是一些常用命令 1.将文件checkout到本地目录 svn checkout path(p ...
- 如何生成可变表头的excel
1.实现功能: 传入一个表头和数据,将数据导入到excel中. 为了便于项目的扩展,数据传入通过泛型集合传入,获取数据时,通过反射的方式获取,这样无论你的表头是多少项,我都能很方便的生成.另外为了便于 ...