Java调⽤dll⽂件的实现解析
⽬录
Java调⽤dll⽂件
环境
接下来进⼊正⽂
Java调⽤dll⽂件⼏种常见⽅式
Java调⽤动态库需要关注的问题
⼀.数据类型对应关系
⼆.Jnative调⽤dll
三.JNA调⽤dll
Java调⽤dll⽂件
近期根据C++做了⼀个图⽚质量检测的项⽬,⽬前需要在在java中进⾏调⽤,所以先在C++上⽣成dll⽂件,然后基于java调⽤dll⽂件实现功能。
环境
C++:VS2017(之前配置opencv真是要了⽼命)
实例化bean的三种方式java:idea2020+jdk1.8。
注意:jdk安装的时候⼩⼼点,path路径容易点编辑,千万别新建,会覆盖的。
接下来进⼊正⽂
1. 创建Java项⽬,假设定义HelloWorld函数,其中“winproject1”是等会要调⽤的dll⽂件,现在进⼊cmd并cd到当前⽬录下⾯,然后javah -jni HelloWorld.HelloWorld,这样会在⽬录下⾯⽣成⼀个HelloWorld_HelloWorld.h。
等会需要将这个头⽂件移到之前安装jdk⽬录⾥的include下⾯,如:D:%你的路径%\Java\include。
2. 打开vs2017,创建⼀下新的控制台项⽬,然后需要配置项⽬包含⽬录的路径,将下⾯两个路径加进去,保险点还可以在附加⽬录⾥⾯加上这些路径。
vs2017中编写上⾯头⽂件中的代码:
JNIEXPORT void JNICALL Java_HelloWorld_HelloWorld_sayHello (JNIEnv *, jobject, jstring, jstring, jstring, jstring) { cout<<"hello world!"<<endl; }
**注:**其中#include "single_check.h"就是我定义检测函数的头⽂件,在下⾯的函数中可以调⽤⾃⼰定义的函数,从⽽让java 执⾏⾥⾯的内容,可以调⽤函数。
3.⽣成第⼀步中提到的winproject1.dll ⽂件,直接点击⽣成-⽣成解决⽅案,便可以在项⽬路径(%项⽬名% 64\Debug\winproject1.dll )中到dll ⽂件,并且复制到jdk 安装路径的bin ⽂件夹下(D:%你的路径%\Java\bin\)
4.最后⼀步最简单啦,在java ⾥⾯直接run 就⾏啦。最后看⼀下结果!完美!
这是windows下⽤java调⽤dll⽂件,接下来要实现跨平台调⽤,得在linux下⽤java可以实现改功能。头秃啊
Java调⽤dll⽂件⼏种常见⽅式
Java调⽤动态库需要关注的问题
1.如何装载DLL⽂件,以及如何定位所要使⽤的⽅法;
2.数据类型如何对应;
3.如何给使⽤的⽅法传递参数;
4.如何获取返回的值。
⼀.数据类型对应关系
Java Type C Type
boolean int
byte char
char wchar_t
short short
double double
float float
String char*
⼆.Jnative调⽤dll
Jnative是对JNI技术进⾏了封装,更加⽅便的让java去调⽤DLL。
1. 下载Jnative库,其中包含JNative.jar, JNativeCPP.dll, JNativeCPP.so这三个包。 JNative.jar是需要导⼊到Java⼯程的lib下, JNativeCPP.dll⽂件放在jdk安装⽬录下,或者是user\System32⽬录下,或者项⽬根⽬录下。
2. 将需要调⽤的dll动态链接库放在SYSTEM32⽂件夹下,或者是项⽬根⽬录下,否则会出现不到dll⽂件的错误。
3. 加载DLL库: System.loadLibrary("TranferEth"); // TransferEth为需要调⽤的DLL⽂件,只需要使⽤DLL⽂件的⽂件名即可。
4. 调⽤DLL⼊⼝函数: JNative jnt = new JNative("TransferEth.dll", "Transfer_Ethernet"); // 参数1为需要调⽤的DLL⽂件,参数2为需要调⽤的⽅法。
5. 设置返回参数类型: jnt.setVal(Type.INT);
6. 设置传⼊参数: jnt.setParameter(0, "TransferScale.ini");
7. 执⾏调⽤: jnt.invoke();
8. 释放资源: jnt.dispose();
三.JNA调⽤dll
JJNA中,它提供了⼀个动态的C语⾔编写的转发器,可以⾃动实现Java和C的数据类型映射。你不再需要编写C动态链接库。
1.在java项⽬中引⼊jna.jar。(当前引⽤的版本为3.4.0 ,之前的版本未提供释放动态链接库资源的⽅法)
2.定义调⽤接⼝,接⼝⽅法与需要调⽤的DLL提供的外部函数⼀致。
例如:DLL⽂件中提供⼊⼝函数:
extern "C" __declspec(dllexport) UINT __stdcall Transfer_Ethernet_EX(char *cTransScale , int nKey);
extern "C" __declspec(dllexport) UINT __stdcall Transfer_Ethernet(char *cTransScale );
则定义的接⼝类中如下:(数据类型对应关系如上)
public interface CallMTScaleLibrary extends Library {
public int Transfer_Ethernet_EX(String filename, int key);
public int Transfer_Ethernet(String filename);
}
3.加载DLL⽂件
String dir = path + "dllName"; // dll⽂件的路径,可以省略后缀名,dll和so后缀都可以加载
CallMTScaleLibrary scaleLibrary = (CallMTScaleLibrary) Native.loadLibrary(dir, CallMTScaleLibrary.class);
4.执⾏调⽤
int result = scaleLibrary.Transfer_Ethernet(path + "TransferScale.ini");
5.释放资源
⼀次调⽤完成后需要释放掉资源,以便后续重复调⽤该资源。通过查看jna的源码Native类中有私有⽅法dispose()可以主动释放掉资源,可以通过反射的⽅式去调⽤该⽅法,也可以重写该⽅法,从⽽实现释放。
private static void dispose(){
NativeLibrary.disposeAll();
nativeLibraryPath = null;
}
注意问题:
1.⽂件路径:DLL⽂件最好放在项⽬的根⽬录下,或者是system32⽂件夹下,必须在环境变量配置的path中。
2.循环调⽤:当循环调⽤同⼀个DLL⽂件时,必须要释放掉上⼀次的资源,否则会被占⽤端⼝(每次调⽤都会在线程⾥进⾏⼀次⽹络通讯)。
以上为个⼈经验,希望能给⼤家⼀个参考,也希望⼤家多多⽀持。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论