1.什么是jna
jna是Java native access的简称,用他可以调用C、C++接口
2.jna pom坐标
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>5.5.0</version>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna-platform</artifactId>
<version>5.5.0</version>
</dependency>
3.jna使用
3.1.准备工作:准备一个C++的dll或者so
3.1.1 代码准备(以下为C++代码)
TestDll.cpp:
代码:
// TestDll.cpp : 定义 DLL 的导出函数。
//
#include "pch.h"
#include "framework.h"
#include "TestDll.h"
#include <iostream>
#include <stdio.h>
using namespace std;
// 这是导出变量的一个示例
TESTDLL_API int nTestDll=0;
// 这是导出函数的一个示例。
TESTDLL_API int fnTestDll(void)
{
return 0;
}
//普通函数测试
TESTDLL_API int add(int a,int b)
{
int value = a + b;
cout << "dll add value=" ;
printf("%d", value);
return value;
}
//被回调函数
TESTDLL_API void jn1() {
printf("*****************111111**************");
}
//被回调函数
TESTDLL_API void jn2() {
printf("*****************222222**************");
}
//被回调函数
TESTDLL_API void jn3() {
printf("*****************333333**************");
}
//测试回调函数
TESTDLL_API void callt(void(*p)(), void(*k)()) {
p();
k();
}
// 这是已导出类的构造函数。
CTestDll::CTestDll()
{
return;
}
TestDll.h:
代码:
// 下列 ifdef 块是创建使从 DLL 导出更简单的
// 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 TESTDLL_EXPORTS
// 符号编译的。在使用此 DLL 的
// 任何项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将
// TESTDLL_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的
// 符号视为是被导出的。
#ifdef TESTDLL_EXPORTS
#define TESTDLL_API __declspec(dllexport)
#else
#define TESTDLL_API __declspec(dllimport)
#endif
// 此类是从 dll 导出的
class TESTDLL_API CTestDll {
public:
CTestDll(void);
// TODO: 在此处添加方法。
};
extern TESTDLL_API int nTestDll;
TESTDLL_API int fnTestDll(void);
extern "C" TESTDLL_API int add(int a, int b);
extern "C" TESTDLL_API void jn1();
extern "C" TESTDLL_API void jn2();
extern "C" TESTDLL_API void jn3();
extern "C" TESTDLL_API void callt(void(*p)(), void(*k)());
3.1.2.打包
打包需要用到vs2019
1.打开新项目
2.选择具有导出功能的dll
3.命名随意,然后点创建
4.把上一步的代码拷进去
5.导出dll
点生成,然后生成解决方案
控制台里面有生成路径,直接去文件夹下可以找到
3.2.java调用
3.2.1.准备环境
将生成的dll放在C:\Windows\System32目录下(linux的so需要放在usr/lib下)
3.2.2.创建接口集成jna里的Library,并根据C++的函数,声明抽象方法
代码如下
package com.jna;
import com.sun.jna.Callback;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.WinNT;
/**
* 功能简述
*
* @author yangsx
* @version 1.0.0
* @date 2021-09-07
*/
public interface MyDll extends Library {
MyDll mydll = (MyDll)Native.load("TestDll.dll",MyDll.class);
//int FACE_GATE_CustomCommand(WinNT.HANDLE hServer, byte[] pData, int nDataLen);
int add(int a,int b);
void jn3();
void callt(Callback callback1, Callback callback2);
interface SCBack1 extends Callback {
void jn1();
}
interface SCBack2 extends Callback {
void jn2();
}
}
注意,回调函数需要直接传入Callback的实例
由于C++的类型与java有所不同,这里贴出对照表
3.2.3.普通方法测试
可以看到java和C++的打印都有,接口调用成功
代码如下
package com.jna;
/**
* 功能简述
*
* @author yangsx
* @version 1.0.0
* @date 2021-09-07
*/
public class Main {
public static void main(String[] args) {
int add = MyDll.mydll.add(1, 2);
System.out.println("MyDll.mydll.add:"+add);
/* MyDll.mydll.jn3();
MyDll.SCBack1 SCBack1= new SCBack1_Impl();
MyDll.SCBack2 SCBack2= new SCBack2_Impl();
MyDll.mydll.callt(SCBack1,SCBack2);*/
}
}
3.2.3.回调函数调用
把上述代码引掉的部份打开进行测试
效果如下
这里传入了SCBack1和SCBack2的实现类,贴一下代码
package com.jna;
/**
* 功能简述
*
* @author yangsx
* @version 1.0.0
* @date 2021-09-08
*/
public class SCBack1_Impl implements MyDll.SCBack1 {
@Override
public void myBack1() {
System.out.println("myBack1:回调成功!");
}
}
package com.jna;
/**
* 功能简述
*
* @author yangsx
* @version 1.0.0
* @date 2021-09-08
*/
public class SCBack2_Impl implements MyDll.SCBack2 {
@Override
public void myBack2() {
System.out.println("myBack2:回调成功!");
}
}
可以看到我们控制台打印了myBack1:回调成功!和myBack2:回调成功!而且都是我们java代码里写的方法。
3.2.4.linux环境下的注意事项
1.so需要放在lib包下,而且打包的时候需要达成libTestDll.so的形式
2.linux的加载和windows也不一样,加载so需要写成
MyDll mydll = (MyDll)Native.load(“TestDll”,MyDll.class);
前面的lib和后面的so都不需要