java利用jna调用dll(史上最全)
内容说明
本文主要是我在做项目中,甲方用的java开发的平台,我们写算法嵌入到他们的系统中,本来以为直接用java写就OK了,可是他们要求用c++写说速度快(说实话只要不操作硬件,现在的java虚拟机运行java和c++的执行效率几乎一样)(没办法,只能使用java调用c++生成的dll),这里就不赘述生成dll的方法了,网上多的很。本文主要介绍利用jna调用dll的具体实现,以及在实现过程遇到的坑,基本上含盖了所有内容。
在这期间我使用过
jna介绍
大白话解释就是jna是用来调用dll的jar包,是对jni技术的一个升级,使用起来比jni方便许多。
用到的工具及资料
1.idea2019创建的java的Maven项目;
2.vs2019生成的dll
3.jna的jar包:因为例子是用Maven项目做的,jar包坐标如下:
<dependency>
<groupId>com.sun.jna</groupId>
<artifactId>jna</artifactId>
<version>3.0.9</version>
</dependency>
4.jna的官方文档如下(看里面的数据类型转换就可以了,其他看我的例子就ok):
链接:https://java-native-access.github.io/jna/4.2.1/overview-summary.html.
有了上面的准备,下面就开始我们的例子了。
dll中的内容
首先用vs2019写的c++dll的程序如下,该例子主要演示java和c++中的char*、int、double的类型对应(其他的去上面链接的官方文档中寻找),以及对应参数是结构体以及结构体中嵌套结构体,函数返回值也为结构体以及结构体中嵌套结构体。
#define dllTest _declspec(dllexport)
#pragma once
#include <map>
#include <vector>
#include <iostream>
#include <string>
using namespace std;
struct myStruct
{
int a;
char* steel;
int b;
};
struct myStructs
{
myStruct mS[10];
int count;
};
extern "C"
{
dllTest myStruct allroundTest(int i, int j, char* s) {
mystru.a = i;
mystru.b = j;
mystru.steel = s;
return mystru;
}
dllTest myStructs* arrayFun(myStructs* ms) {
return ms;
}
dllTest char* returnChar(myStructs* ms, int i) {
char* res = ms->mS[i].steel;
return res;
}
dllTest char* returnString(char* ms) {
return ms;
}
dllTest int addNormal(myStruct ms) {
return ms.b + ms.a;
}
dllTest int* test01(int* a) {
return a;
}
dllTest void addPrt(myStruct* ms, int* ret) {
*ret = ms->a + ms->b + 1;
}
dllTest int addRef(myStruct& ms) {
return ms.a + ms.b + 2;
}
}
java中利用jna模拟dll中的结构体(利用接口中的内部类模拟)
注意,其中对应结构体中的属性的顺序以及属性名必须和dll中保持一致,还有注意dll存放的位置,我是直接放到jdk的bin目录下,结构体中的ByValue以及ByReference分别对应c++中的引用和指针。
package com.tz;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Structure;
import com.sun.jna.ptr.IntByReference;
public interface dllTestApi extends Library {
//需将dll放进jdk的bin目录下
public static dllTestApi dta = (dllTestApi) Native.loadLibrary("PanAlgDLL", dllTestApi.class);
public static class myStructur extends Structure {
public static class ByReference extends myStructur implements Structure.ByReference{
}
public static class ByValue extends myStructur implements Structure.ByValue{
}
public int a;
public String steel;
public int b;
}
public static class myStructurs extends Structure {
public static class ByReference extends myStructurs implements Structure.ByReference{
}
public static class ByValue extends myStructurs implements Structure.ByValue{
}
public myStructur.ByValue[] structs = new myStructur.ByValue[10];
public int count;
}
int addNormal(myStructur.ByValue ms);
void addPrt(myStructur.ByReference pms, IntByReference sum);
int addRef(myStructur.ByReference rms);
myStructurs.ByReference arrayFun(myStructurs.ByReference ms);
String returnString(String ms);
IntByReference test01(IntByReference a);
String returnChar(myStructurs.ByReference ms, int i);
myStructur.ByValue allroundTest(int i, int j, String s);
}
调用dll主程序
下面是调用dll中的方法的测试程序:
package com.tz;
import com.sun.jna.ptr.IntByReference;
public class Test01 {
public static void main(String[] args) {
System.setProperty("jna.encoding", "GBK");
dllTestApi.myStructur.ByReference pmysStructur = new dllTestApi.myStructur.ByReference();
pmysStructur.a = 1;
pmysStructur.b = 3;
IntByReference ib = new IntByReference();
dllTestApi.dta.addPrt(pmysStructur,ib);
System.out.println(ib.getValue());
System.out.println(dllTestApi.dta.addRef(pmysStructur));
dllTestApi.myStructur.ByValue vmysStructur = new dllTestApi.myStructur.ByValue();
vmysStructur.a = 1;
vmysStructur.b = 3;
System.out.println(dllTestApi.dta.addNormal(vmysStructur));
IntByReference ibf = new IntByReference(2);
ibf.setValue(3);
ibf.setValue(4);
IntByReference res = dllTestApi.dta.test01(ibf);
int[] re = res.getPointer().getIntArray(0, 3);
System.out.println(re[0]);
dllTestApi.myStructur.ByValue pmys1 = new dllTestApi.myStructur.ByValue();
pmys1.b = 1;
pmys1.steel = "abcf";
pmys1.a = 1;
dllTestApi.myStructur.ByValue pmys2 = new dllTestApi.myStructur.ByValue();
pmys2.a = 2;
pmys2.steel = "asdg";
pmys2.b = 2;
dllTestApi.myStructurs.ByReference pmyss = new dllTestApi.myStructurs.ByReference();
pmyss.structs[0] = pmys1;
pmyss.structs[1] = pmys2;
pmyss.count = 2;
dllTestApi.myStructurs.ByReference ress = dllTestApi.dta.arrayFun(pmyss);
System.out.println(ress.count);
System.out.println(ress.structs[1].a);
String rS = dllTestApi.dta.returnChar(pmyss, 0);
System.out.println(rS);
String str11 = dllTestApi.dta.returnString("666");
System.out.println(str11);
dllTestApi.myStructur.ByValue reess = dllTestApi.dta.allroundTest(6, 6, "tz");
System.out.println(reess.a);
System.out.println(reess.steel);
}
}
利用jna调用dll需要注意的问题
1、想要调用dll中的方法,且调用的方法需要结构体数组参数或者返回值是结构体数组,我们知道c++方法对于结构体数组参数处理起来比较麻烦,所以我解决的办法就是加一个结构体,该结构体中包含结构体数组以及包含的个数,如上面的例子所示;
2、当java要接收dll方法返回结构体参数,尤其当结构体中包含char*时,我们需要在java中将结构体实例化当作参数传入要调用的dll中,并在dll中将要返回的结构体传给java中当作参数的结构对象,不然会报莫名其妙的错(我调试了好久才发现的)。
总结
以上就是java利用jna调用dll的小例子,例子虽小,五脏俱全,基本上涵盖了所有的内容。研究不易,如果该文章对您有帮助,希望