Java调用C++的动态库

最近在研究动态库调用的问题,想用Java调用我写好的C++的动态库。经过不断地尝试,终于实现了我想要的功能,网上也看了很多资料,最终整理出来这个文档,希望对有需要的朋友有帮助。

JNI

JNI是Java Native Interface的缩写,通过使用 Java本地接口书写程序,可以确保代码在不同的平台上方便移植。 从Java1.1开始,JNI标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。总之就一句话,要用Java调用第三方的动态库,用JNI就对了!

Java要调用第三方的动态库,通俗点说就是需要将这个第三方的动态库按照Java语言的要求再封装一遍,变成Java可以调用的新动态库,这个新动态库去调用原始的动态库。
在这里插入图片描述

步骤

1、编写带有native声明的方法的java类,该方法与真正要调用的动态库的方法的参数和返回值均一致。

2、编写测试类调用前面编写好的类里面的方法,最后会运行这个测试程序测试动态库调用。

3、使用javac命令编译第1步所编写的java类成.class文件

4、然后使用javah + java类名生成扩展名为.h的头文件

javah -d targetDir -classpath . 包路径+类名(类名不包含文件后缀名)

5、新建C++动态库项目,使用C++实现上面生成的头文件中的本地方法

6、将C++编写的文件生成动态库

7、将这个动态库以及第三方动态库库拷贝到Java工程根目录中

第3、4步骤根据JDK版本不同有所区别,JDK1.8以上没有javah,已经集成到javac命令中,因此JDK1.8以上版本可将这两步合并为一步,直接执行

javac -encoding utf8 -h targetDir sourceFile

-encoding utf8 指定源文件编码格式

-h targetDir 头文件的输出目录,. 表示当前目录,后面必需加个空格

sourceFile 源文件,带后缀,如:JniDLL.java

环境

我的测试环境及开发工具如下:

  • JDK11.0.2
  • IDEA
  • VS2013
  • 64位Windows

实例操作

我新建了一个Java项目,这个过程就不说了,大家都会。

1、JniDLL.java 用于加载即将重新实现的新动态库

package JniCallDLL;

public class JniDLL {
    public native int add(int a,int b);//在我写的实际需要调用的动态库中有加减乘除几个测试方法,我这里只测试了其中的add方法
    //如果有些代码必须在项目启动的时候就执行的时候,需要使用静态代码块,这种代码是主动执行的
    //需要在项目启动的时候就初始化,在不创建对象的情况下,其他程序来调用的时候,需要使用静态方法,这种代码是被动执行的.
    //静态代码块只能定义在类里面,它独立于任何方法,不能定义在方法里面。
    //静态代码块里面的变量都是局部变量,只在本块内有效。
    //静态代码块会在类被加载时自动执行,而无论加载者是JVM还是其他的类。
    //一个类中允许定义多个静态代码块,执行的顺序根据定义的顺序进行。
    //静态代码块只能访问类的静态成员,而不允许访问实例成员。
    static {
        System.loadLibrary("TestJniDLL");//这是我即将要重新实现的动态库名字
    }
}

2、测试类RunDllCall.java,调用上面的类中的方法

package JniCallDLL;

public class RunDllCall {
    public static  void main(String[] args)
    {
        JniDLL jniDll = new JniDLL();
        System.out.println(jniDll.add(2,3));
    }
}

其实这里完全可以两步合并到一起,我为什么要在这儿分成两个文件呢,从第一个源文件的注释就可以看出来,毕竟我不可能白写那么多注释,其实就是为了介绍一下static{},这个静态代码块。如果要写到一起的话如下:

package JniCallDLL;

public class JniDLL {
    public native int add(int a,int b);//在我写的实际需要调用的动态
    public static  void main(String[] args)
    {
    	System.loadLibrary("TestJniDLL");//这是我即将要重新实现的动态库名字
        JniDLL jniDll = new JniDLL();
        System.out.println(jniDll.add(2,3));
    }
}

3、来到我的Java源文件目录
在这里插入图片描述
执行如下命令:

javac -encoding utf8 -h . JniDLL.java

在这里插入图片描述
将在当前目录生成.h的头文件,文件名格式为:包名_类名.h
在这里插入图片描述
至此,Java这边的开发就完成了,接下来让我们来到VS。

首先我们需要制作一个需要被调用的原始第三方动态库,这个过程就不说了,自行百度,如何制作一个C++动态库。嫌麻烦的话,也可以直接拿别人做的动态库过来用。我这里自己写的第三方动态库叫MyMFCLibrary.dll,我会把所有源码上传,需要的可以下载。

接下来我们需要制作一个可被Java调用的动态库,并且在这个动态库中调用第三方动态库,完成整个Java调用流程。

1、创建一个C++ 的Win32空项目
我这里这么建,你完全可以不这么建,方法不同没关系,看疗效。
在这里插入图片描述
在这里插入图片描述
添加一个TestJniDLL类,注意类名和工程名,不管怎么写,只要生成出来的动态库叫TestJniDLL就行,为什么要叫这个名,因为Java里面调用的是这个动态库,所以名字要一致,不然咋调用成功。
在这里插入图片描述
在这里插入图片描述

拷贝以下三个文件到TestJniDLL项目的头文件目录下

  • jni.h 在你的JDK目录的include目录下
  • jni_md.h 在JDK目录的include的win32目录下
  • JniCallDLL_JniDLL.h 这就是前面Java里面生成的头文件
    在这里插入图片描述
    打开JniCallDLL_JniDLL.h,有报错,将#include <jni.h>修改为#include “jni.h”,因为你这个头文件不是系统自己的标准头文件,不在系统默认的路径下,当然不能用<>来包含了。
    在这里插入图片描述
    修改TestJniDLL.cpp,加入如下代码
#include "JniCallDLL_JniDLL.h"
#include <Windows.h> //HMODULE
#include <tchar.h> //_T

typedef int(*Dllfun)(int , int);//定义一个名为Dllfun的返回值和两个参数都为int的函数指针
JNIEXPORT jint JNICALL Java_JniCallDLL_JniDLL_add
(JNIEnv *, jobject , jint a, jint b) {
	HMODULE hinst=LoadLibrary(_T("MyMFCLibrary.dll"));//加载第三方动态库,不带路劲的话,默认就在当前动态库的相同路径下,带路径的话,路径的'\'需要写成'\\'
	if (hinst==NULL)
	{
		return 0;//调用第三方动态库失败
	}
	Dllfun dllfun = (Dllfun)GetProcAddress(hinst, "add");//获取第三方动态库中的add()方法
	int result = dllfun(a, b);//调用add()方法
	return result;
}

在这里插入图片描述

基本开发就完成了,但是还需要做一个配置,Java调用动态库时,你的Java运行环境和你的第三方动态库以及自己封装的动态库必须在相同的平台下,否则就会出现找不到动态库或者无法正常调用的情况。比如我的系统是64位的,那我Java的运行环境就是64位的,我的第三方动态库和我封装的都必须是在64位平台,需要做如下配置:

在这里插入图片描述
在这里插入图片描述

没有x64的话,选择新建
在这里插入图片描述
我这里的第三方库是我自己写的,所以可以改,如果是别人写的,那就改不了,只能改你的环境去适应他的动态库或者参考其他大神写的这篇文章《64位进程调用32位dll的解决方法 / 程序64位化带来的问题和思考》

OK!完成,重新生成一下解决方案,把自己的两个动态库拷贝到Java工程目录下就可以运行啦。
在这里插入图片描述
在这里插入图片描述
3+2=5 调用成功!

源码下载

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值