Java调用C++动态链接库dll,有详细过程。VS2015+Eclipse以及失败解决方案

Posted 倒着走的码农

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java调用C++动态链接库dll,有详细过程。VS2015+Eclipse以及失败解决方案相关的知识,希望对你有一定的参考价值。

背景
由于之前一直没有接触过用Java调用C++,目前正在考虑用C++写主要的算法,然后用Java来调用。通过查找资料,发现要嘛用通信的方式,要嘛就使用JNI,也就是Java Native Interface的简称,中文是“Java本地调用”。通常在Java程序中的函数可以调用Native语言写的函数,Native一般指的是C/C++编写的函数。
Native程序中的函数可以调用Java层的函数,也就是说在C/C++程序中可以调用Java的函数。使用JNI的目的是为了屏蔽不同操作系统平台的差异性,通过Java语言来调用Native语言的功能模块。
本次博主将通过详细截图的形式来演示如何使用Java调用C++dll。按照截图完成,保证可以用。
附上相关的两个项目源码
http://download.csdn.net/detail/buptzhengchaojie/9553327
http://download.csdn.net/detail/buptzhengchaojie/9553328

另外还有一篇关于Java webservice里调用依赖其他dll 的自定义dll,如果需要,这里有传送门:

Java webservice 调用自定义dll(有其他依赖)方法全解,无需设置环境变量

一、新建Java工程,在Java类中声明一个native的方法

新建Java项目

这里写图片描述

在新建的项目中创建packet(包),并且在包下创建一个Class(类)。

这里写图片描述

接下来,在该类中添加如下代码:

public class JavaInvodeCPlus {

    //声明为native,表明是有外部来实现的

    public native String returnHelloWorldToUpcase(String string);

    public native void sayHelloWolrd();
}

二、使用Javah命令生成native方法的声明的C/C++头文件

进入该项目所在的位置,博主这里的位置是D:\\00Coding\\my-space\\JavaInvokeCPlus
这里写图片描述

接着进入bin目录下,找到该packet下存在一个.class文件

这里写图片描述

我们需要用javah命令来生成头文件。回到bin目录下,因为这里涉及到包名,所以必须在包目录下来。按住shift键,同时在文件夹内空白处右击,可以进入命令行。当然,你也可以一步步进入到该路径下。

这里写图片描述

这里写图片描述

这里需要注意的是,文件的末尾不加上.class后缀。然后我们可以看到在bin目录下多了一个.h头文件。
打开我们可以看到如下代码:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_cjzheng_service_JavaInvodeCPlus */

#ifndef _Included_com_cjzheng_service_JavaInvodeCPlus
#define _Included_com_cjzheng_service_JavaInvodeCPlus
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_cjzheng_service_JavaInvodeCPlus
 * Method:    returnHelloWorldToUpcase
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_cjzheng_service_JavaInvodeCPlus_returnHelloWorldToUpcase
  (JNIEnv *, jobject, jstring);

/*
 * Class:     com_cjzheng_service_JavaInvodeCPlus
 * Method:    sayHelloWolrd
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_cjzheng_service_JavaInvodeCPlus_sayHelloWolrd
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

这就是头文件的内容,现在我们来分析一下这个头文件的结果。有图有真相:

这里写代码片

三、引入生成的C++头文件来编写C++源文件

博主这里使用的VS2015,就是喜欢用新的软件。任性也是一种罪过…

新建项目

这里写图片描述

点击确定后,点击下一步,进入如下界面

这里写图片描述

点击完成,进入。

这里写图片描述

接着我们需要引入的头文件有三个,一个是刚刚使用javah生成的头文件,剩下两个需要在JDK中拷贝,博主这里使用的是JDK7的,现在将这三个头文件拷贝到C++工程的目录下。jdk的头文件在jdk的安装目录下,这两个文件的目录如下:

这里写图片描述
这里写图片描述

得到C++项目的文件如下:

这里写代码片

然后将这三个头文件导入vs2015中

这里写图片描述
这里写图片描述
这里写图片描述

这里写图片描述

此时我们看到,貌似报错了呀!别着急,想必学过C++的人都知道这是系统库和自定义库的区别。这里将<>改成“”就可以啦!

这里写图片描述

接着我们需要做的就是实现这两个需要实现的方法,新建一个cpp文件

这里写图片描述

引入我们生成的.h文件,然后实现它。

这里写图片描述
附上代码:

#include<iostream>
#include"com_cjzheng_service_JavaInvodeCPlus.h"
#include <string>
#include <cctype>
#include <algorithm>
using namespace std;

/*
* Class:     com_cjzheng_service_JavaInvodeCPlus
* Method:    returnHelloWorldToUpcase
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_cjzheng_service_JavaInvodeCPlus_returnHelloWorldToUpcase
(JNIEnv *, jobject, jstring str) {
    return str;
}

/*
* Class:     com_cjzheng_service_JavaInvodeCPlus
* Method:    sayHelloWolrd
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_cjzheng_service_JavaInvodeCPlus_sayHelloWolrd
(JNIEnv *, jobject) {
    cout << "Hello World" << endl;
}

这里写图片描述

报这个提示,不要惊慌,关闭就行。只要底下编译成功就可以了。

这里写图片描述

到该项目的路径下,我们可以找到生成了一个dll文件

这里写图片描述

四、将DLL文件加入到PATH环境变量下

这里有两种方法,一种是在环境变量中path里追加上这个dll所在的路径;一种是将这个dll文件拷贝到已经存在于path中的路径下。博主这里采用后者,因为博主的jdk配置了环境变量。所以直接将该dll拷贝到jdk的bin目录下。

这里写图片描述

所以我这里就将这个dll拷贝到jdk8的路径下,读者不要混淆,虽然博主这里采用jdk7,但是那是eclipse指定的项目jdk环境,是可以指定的,不要和环境变量这个混淆。

这里写图片描述

好了,离成功不远了!但是越是到快成功的时候,越要耐住性子。

五、Java类中加载DLL,然后调用声明方法
回到Java项目中,写一个测试类,调用该dll,执行相应方法,就可以啦!

这里写图片描述

这里写图片描述
附上代码:

public class TestMain {

    /**
     * @Title: TestMain
     * @Description: TODO
     * @param args
     *            void
     * @Date: 2016年6月18日
     * @author:zhengchaojie
     */
    public static void main(String[] args) {
        System.loadLibrary("CplusImplement");// 不需要加入.dll后缀
        JavaInvodeCPlus javaInvodeCPlus = new JavaInvodeCPlus();
        System.out.println(javaInvodeCPlus.returnHelloWorldToUpcase("QQQQQQQ"));
    }

}

很幸运,成功了,而且不带有任何麻烦。bingo!!!
六、常见失败

1、找不到指定的dll文件:

Exception in thread "main" java.lang.UnsatisfiedLinkError: no CplusImplement in java.library.path
    at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1886)
    at java.lang.Runtime.loadLibrary0(Runtime.java:849)
    at java.lang.System.loadLibrary(System.java:1088)
    at com.cjzheng.service.TestMain.main(TestMain.java:20)

解决方案:确认该dll文件名是否正确,是否在环境变量中添加了可以找到该dll的变量。

2、方法名不对,或者参数个数不对,或者参数形式不对(这里是博主另外一个项目的错误)

Exception in thread "main" java.lang.UnsatisfiedLinkError: com.cjzheng.util.CPlusMethod.SAASChooseAntenna(DDDDDDDDDD)I

这里写图片描述

3、由于A机子上不同C++编译器生成的dll,在B机器上缺少相应的dll文件导致错误;

Exception in thread "main" java.lang.UnsatisfiedLinkError: D:\\Program Files\\Java\\jdk1.8.0_60\\bin\\CPlusMethod.dll: Can't find dependent libraries
at java.lang.ClassLoader$NativeLibrary.load(Native Method)
at java.lang.ClassLoader.loadLibrary1(ClassLoader.java:1965)
at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1890)
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1880)
at java.lang.Runtime.loadLibrary0(Runtime.java:849)
at java.lang.System.loadLibrary(System.java:1088)
at com.cjzheng.util.CPlusMethod.<clinit>(CPlusMethod.java:12)
at com.cjzheng.service.impl.TestMain.main(TestMain.java:21)

这里介绍一种比较简便的方法来解决这个问题。
例如如果A机器是VS2010,该DLL是使用VS2010编译,在本机上测试通过,但换了一个机子,就报上面的错误。如果报这个错误,首先可以肯定的是,库得路径都是对的。这时候,如果你去安装VS2010,问题肯定就解决了,但是VS2012装起来太麻烦了。所以这里的解决方法是在A机器上使用VS2010编译C++的DLL时,去掉/MD选项。具体步骤:

这里写图片描述这里写图片描述

这样就可以做到,在B机器上不安装VS2010就可以使用dll啦!

以上是关于Java调用C++动态链接库dll,有详细过程。VS2015+Eclipse以及失败解决方案的主要内容,如果未能解决你的问题,请参考以下文章

nodejs调用C++动态链接库

JAVA调用动态链接库(dll)

vb.net如何调用dll(动态链接库)

如何在C语言中调用C++做的动态链接库

VS c++调用第三方库

08、electron调用C#类库的dll