Java使用JNA调用C/C++DLL库

1.0 使用Visual Studio 2019编写并生成动态链接库

1.1 创建新项目

在这里插入图片描述

1.2 配置编译平台

根据各自需求选择x64或x86(32位),要与JDK版本匹配才行,不然Java那边编译会报错。
在这里插入图片描述

1.3 编写测试代码

新建项目自带framework.h,pch.h,dllmain.cpp,pch.cpp,我这边只编写了pch.h,pch.cpp这两个文件生成测试接口。

IMPORT_DLL这个宏是声明函数输入,输出。函数、类、数据的声明前加上_declspec(dllexport)的修饰符,表示输出。__declspec(dllexport)在C调用约定、C编译情况下可以去掉输出函数名的下划线前缀。extern "C"使得在C++中使用C编译方式进行兼容。在"C++"下定义"C"函数,需要加extern "C"关键词。用extern "C"来指明该函数使用C编译方式。输出的"C"函数可以从"C"代码里调用。

这边主要测试函数调用,直接对结构体赋值,获取。

pch.h

#ifndef PCH_H
#define PCH_H

#include "framework.h"
#include <string.h>
#include <stddef.h>

#ifdef IMPORT_DLL
#else
#define IMPORT_DLL extern "C" _declspec(dllimport)
#endif

typedef struct {
	int a;
	int b;
	int c;
} CardStruct;

IMPORT_DLL void add();
IMPORT_DLL void set_struct(CardStruct *cs);
IMPORT_DLL CardStruct getCardStruct();

#endif

pch.cpp

#include "pch.h"
#include <iostream>

CardStruct mCs;

void add()
{
	mCs.c = mCs.a + mCs.b;
	std::cout << "C++ c: " << mCs.c << "\n";
}

CardStruct getCardStruct()
{
	return mCs;
}

void set_struct(CardStruct *cs)
{
	mCs.a = cs->a;
	mCs.b = cs->b;
	mCs.c = cs->c;
}

1.4 生成解决方案DLL提取位置

选择生成->生成解决方案
在这里插入图片描述

2.0 Java使用JNA进行DLL库调用

2.1 导入JNA jar包

创建一个Java程序,右键Build Path->Configure Build Path
我这边只导入了jna-4.0.0.jar,jna-platform-4.0.0.jar,版本差异可忽略。自行下载,能用就行。
在这里插入图片描述

2.2 导入DLL动态链接库

我这边直接放在项目根目录
在这里插入图片描述

2.3 Java端验证代码

public class Test {

	public interface CLibrary extends Library {
		CLibrary INSTANCE = (CLibrary) Native.loadLibrary("xxxDll", CLibrary.class);

		int add();
		int test();

		public static class ExtU_DEMO_T extends Structure {

			public static class ByReference extends ExtU_DEMO_T implements Structure.ByReference {
			}

			public static class ByValue extends ExtU_DEMO_T implements Structure.ByValue {
			}

			public int a;
			public int b;
			public int c;

			@Override
			protected List getFieldOrder() {
				List<String> Field = new ArrayList<String>();
				Field.add("a");
				Field.add("b");
				Field.add("c");
				return Field;
			}

		}
		void set_struct(ExtU_DEMO_T.ByReference csbr);
		ExtU_DEMO_T.ByValue getCardStruct();
	}

	public static void main(String[] args) {
		Test.CLibrary.ExtU_DEMO_T.ByReference tccb = new Test.CLibrary.ExtU_DEMO_T.ByReference();
		tccb.a = 12;
		tccb.b = 22;
		CLibrary.INSTANCE.set_struct(tccb);// 设值给DLL
		CLibrary.INSTANCE.add();// 进行运算
		System.out.println("java c: " + tccb.c);
		
		Test.CLibrary.ExtU_DEMO_T.ByValue tcet = CLibrary.INSTANCE.getCardStruct();
		System.out.println("getCardStruct a: " + tcet.a);
		System.out.println("getCardStruct b: " + tcet.b);
		System.out.println("getCardStruct c: " + tcet.c);
	}

}

运行结果:
java c: 0
getCardStruct a: 12
getCardStruct b: 22
getCardStruct c: 34
C++ c: 34

2.4 Java端指针方式操作DLL结构体

public static class AAA extends Structure {
	public AAA() {
		super();
	}

	public AAA(Pointer p) {
		super(p);
	}

	public static class ByReference extends AAA implements Structure.ByReference {
	}

	public static class ByValue extends AAA implements Structure.ByValue {
	}

	public int a;
	public int b;
	public int c;

	@Override
	protected List getFieldOrder() {
		List<String> Field = new ArrayList<String>();
		Field.add("a");
		Field.add("b");
		Field.add("c");
		return Field;
	}
}
// 获取指针地址
Pointer cs = lib.getGlobalVariableAddress("CardStruct");
// 传入指针
AAA a1 = new AAA(cs);
a1.read();
// 给结构体直接赋值
a1.a = 1;
a1.write();
a1.b = 2;
a1.write();
a1.c = 3;
a1.write();

2.5 另一种方式执行函数

这种方式就无需在Java端重复声明int add();

NativeLibrary lib = NativeLibrary.getInstance("xxx.dll");
Function initialize = lib.getFunction("add");
initialize.invoke(new Object[] {});

2.6 使用过程中需要注意事项

  • JDK版本要与DLL生成环境匹配,Java --version可查看jdk环境。
  • loadLibrary路径可调整,并非只能放在工程根目录。
  • extends Library后声明函数,变量要与DLL顺序要对上,据说顺序乱会出问题。
  • 经实际验证,只有通过指针方式才能直接对DLL立面的结构体进行赋值。不然只能改DLL,声明set函数才能赋值。

3.0 JNI与JNA的差异

编写JNI需要集成NDK环境进行编译,生成函数库的头文件,一般提供so/dll都会提供头文件,生成一个native环境下的头文件才能给Java端进行调用。JNI有许多JNI定义的变量类型,操作相对JNA要复杂很多。JNA(Java Native Access)是一个开源的Java框架,是Sun公司推出的一种调用本地方法的技术,是建立在经典的JNI基础之上的一个框架。之所以说它是JNI的替 代者,是因为JNA大大简化了调用本地方法的过程,使用很方便,基本上不需要脱离Java环境就可以完成。

之前写过的一篇关于JNI的博客(Linux环境下编写的demo):https://blog.csdn.net/u012169524/article/details/50830426

注:下文中的 *** 代表文件名中的版本号。 # 【jna-***.jar中文文档.zip】 中包含: 中文文档:【jna-***-javadoc-API文档-中文(简体)版.zip】 jar包下载地址:【jna-***.jar下载地址(官方地址+国内镜像地址).txt】 Maven依赖:【jna-***.jar Maven依赖信息(可用于项目pom.xml).txt】 Gradle依赖:【jna-***.jar Gradle依赖信息(可用于项目build.gradle).txt】 源代码下载地址:【jna-***-sources.jar下载地址(官方地址+国内镜像地址).txt】 # 本文件关键字: jna-***.jar中文文档.zip,java,jna-***.jar,net.java.dev.jna,jna,***,com.sun.jna,jar包,Maven,第三方jar包,组件,开源组件,第三方组件,Gradle,中文API文档,手册,开发手册,使用手册,参考手册 # 使用方法: 解压 【jna-***.jar中文文档.zip】,再解压其中的 【jna-***-javadoc-API文档-中文(简体)版.zip】,双击 【index.html】 文件,即可用浏览器打开、进行查看。 # 特殊说明: ·本文档为人性化翻译,精心制作,请放心使用。 ·只翻译了该翻译的内容,如:注释、说明、描述、用法讲解 等; ·不该翻译的内容保持原样,如:类名、方法名、包名、类型、关键字、代码 等。 # 温馨提示: (1)为了防止解压后路径太长导致浏览器无法打开,推荐在解压时选择“解压到当前文件夹”(放心,自带文件夹,文件不会散落一地); (2)有时,一套Java组件会有多个jar,所以在下载前,请仔细阅读本篇描述,以确保这就是你需要的文件; # Maven依赖: ``` <dependency> <groupId>net.java.dev.jna</groupId> <artifactId>jna</artifactId> <version>***</version> </dependency> ``` # Gradle依赖: ``` Gradle: implementation group: 'net.java.dev.jna', name: 'jna', version: '***' Gradle (Short): implementation 'net.java.dev.jna:jna:***' Gradle (Kotlin): implementation("net.java.dev.jna:jna:***") ``` # 含有的 Java package(包)(此处仅列举3个): ``` com.sun.jna com.sun.jna.internal com.sun.jna.platform ...... ``` # 含有的 Java class(类)(此处仅列举3个): ``` com.sun.jna.AltCallingConvention com.sun.jna.Callback com.sun.jna.Callback.UncaughtExceptionHandler ...... ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值