在很多场景中,需要用java来调用c++动态链接库,在windows中是.dll文件,linux中是.so文件,本篇介绍使用qt或vs编译动态链接库后,java调用的示例,以windows平台为例。
1.qt编译dll
1.1开发环境
qt5.12.9
1.2.新建项目
打开Qt Creator,文件->新建文件或项目,选择library->c++ Library
输入项目的名称,然后一直下一步,默认的就行,到Kits选择时,需要注意,你是准备在32位平台上跑,还是在64位平台上跑,这很重要,或者你两个版本都需要,选择相应的编译器。此处我选择了2个,两个版本都编译一个,后面有坑。
其他默认下一步,项目建好后,默认有4个文件:项目文件1,头文件2个,源文件1个。
我们主要关心firstdll.h和first.cpp即可。下面实现一个加法函数如下,首先在头文件中声明,注意1.要把声明写在类的外面,写里面不行;注意2.要在声明中加上extern "C" 关键字,当java调用时,告诉程序,下面的函数要用C语言来编译了,其实就是一种约定
完整头文件firstdll.h如下
#ifndef FIRSTDLL_H
#define FIRSTDLL_H
#include "firstDll_global.h"
class FIRSTDLL_EXPORT FirstDll
{
public:
FirstDll();
};
#endif // FIRSTDLL_H
extern "C"{
int sum(int a,int b);
}
然后来写源文件,即函数的实现,注意函数前要加上Q_DECL_EXPORT关键字,告诉程序,要把这个函数导出到链接库里。在这个函数里,我不仅实现了加法,还打印了结果,完整的firstdll.cpp如下
#include "testdll.h"
#include<iostream>
using namespace std;
TestDll::TestDll()
{
}
Q_DECL_EXPORT int sum(int a,int b)
{
cout<<"a+b="<<a+b<<endl;
return a+b;
}
好了,设置一下项目的构建路径,先采用release模式编译
点击构建,编译输出显示正常退出,找到你刚刚设置的目录,看到有3个文件,dll就是动态链接库了
2.java调用dll
这里使用jna调用dll,先在maven中添加包依赖
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>5.12.1</version>
</dependency>
新建一个类,类中新建一个接口继承Library类,接口中使用Native.load方法先加载dll,该方法第1个参数是dll的绝对路径,第2个参数是接口的类名,作用就是把dll和这个接口连接起来。然后再实现dll中的函数,需要注意的是C语言中的类型和java中的类型是不一样的,官方给出了一个映射表,例如c++中int映射到Java中的int,c++中的char*映射到java中String,其他还有更复杂的请另行查找学习,实现代码如下
import com.sun.jna.Library;
import com.sun.jna.Native;
public class testJna {
public interface Mydll extends Library{
//加载dll
Mydll mydll=(Mydll) Native.load("D:\\QtWorkSpace\\testDll\\release\\testDll.dll",Mydll.class);
//实现dll中的函数
int sum(int a,int b);
}
}
接下来写一个main函数直接调用,代码如下
public static void main(String[] args){
System.out.println(Mydll.mydll.sum(1,2));
}
看一下结果
a+b=3
3
第1行,a+b=3是dll中的函数打印的结果,第2行,3是调用dll函数java打印的返回值。
3.异常排除
3.1环境变量缺失
Exception in thread "main" java.lang.UnsatisfiedLinkError: Unable to load library 'D:\QtWorkSpace\testDll\release\testDll.dll':
%1 不是有效的 Win32 应用程序。
%1 不是有效的 Win32 应用程序。
Native library (win32-x86-64/D:\QtWorkSpace\testDll\release\testDll.dll) not found in resource path
Suppressed: java.lang.UnsatisfiedLinkError: %1 不是有效的 Win32 应用程序。
遇到这种错显示动态库加不进来,查看你的dll是否依赖于不在系统变量中的dll,举个例字,你的dll使用cout来打印文字,引用的iostream库,编译后的dll他只有你导出的函数,并不包含这个iostream,当dll运行的时候,还是要去系统中找这个依赖的,这里的依赖是libstdc++-6.dll,巧了你的系统中有个libstdc++-6.dll,但他是32位的,与你编译的64位的不兼容,就报错。归根到底是系统中没有正确的库,那这个库在哪呢?在你的编译器的lib里,而这个库你没有添加到环境变量里,就不能加载了。
解决方案:
1.缺什么库就添加什么库,一般你用qt编译的,库就在编译器的lib下面,我的目录就是E:\Qt\Qt5.12.9\Tools\mingw730_64\x86_64-w64-mingw32\lib,把这个目录添加到path环境变量中,重启IDE生效,再运行一次,加载dll的时候,就会到这个lib下面找到所需要的依赖,就可以正常运行了。
2.你的dll本身就是32位的与你的64位的jdk不一致,需要两者保持一致。