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:

java调C的库函数传递数组_java


代码:

// 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:

java调C的库函数传递数组_API_02


代码:

// 下列 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.打开新项目

java调C的库函数传递数组_API_03


2.选择具有导出功能的dll

java调C的库函数传递数组_回调函数_04


3.命名随意,然后点创建

4.把上一步的代码拷进去

java调C的库函数传递数组_API_05


5.导出dll

java调C的库函数传递数组_java_06


点生成,然后生成解决方案

java调C的库函数传递数组_java_07


控制台里面有生成路径,直接去文件夹下可以找到

java调C的库函数传递数组_java_08

3.2.java调用

3.2.1.准备环境

将生成的dll放在C:\Windows\System32目录下(linux的so需要放在usr/lib下)

3.2.2.创建接口集成jna里的Library,并根据C++的函数,声明抽象方法

java调C的库函数传递数组_API_09


代码如下

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有所不同,这里贴出对照表

java调C的库函数传递数组_java调C的库函数传递数组_10

3.2.3.普通方法测试

java调C的库函数传递数组_java_11

可以看到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.回调函数调用

把上述代码引掉的部份打开进行测试

效果如下

java调C的库函数传递数组_回调函数_12


这里传入了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都不需要