JNA调用DLL动态库以及如何创建DLL库详解

JNA调用DLL动态库以及如何创建DLL库

JNA与JNI这里就不比较了,简而言之,jna方便简洁。下面直奔主题,先从创建dll开始,第二部分介绍jna的使用。

一、创建DLL动态库,使用C++,开发平台用的是VS2015.

新建项目:

1、新建VC++Win32控制台应用程序

在这里插入图片描述

2、点击确定选择DLL,空项目

在这里插入图片描述

3、项目创建完成后,在项目栏源文件右键创建.cpp文件

4、在项目栏头文件中创建.h头文件,文件名跟cpp保持一致(创建时会有提示选择)

在这里插入图片描述

5、写简单的demo测试

在这里插入图片描述
代码:

#include"ygt.h"
int add(int a, int b) {
	return a + b;
}
char* print() {
	return "helloword";
}

6、在头文件声明函数,注意格式必须extern “C”,并且以_declspec(dllexport)前缀。否则java调用不了(这方面理解不深,多次踩坑确实如此,这是bug报错点之一)

在这里插入图片描述
代码:

#pragma once
extern "C" _declspec(dllexport)int add(int a, int b);
extern "C" _declspec(dllexport)char* print();

7、选择X64,我的电脑是win7,64位。

在这里插入图片描述

8、相关配置设置

(1)、在右侧项目栏,右键项目,我的名字叫ygt,右键点击属性,点开C/C++,点击代码生成,把右侧运行库删除为空,不然java调取不到,(这是bug报错点之一)。

在这里插入图片描述

(2)、点击链接器,点击常规,在右侧看到链接库依赖项,选择为否,不然java可能调取不了,(这是bug报错点之一)

在这里插入图片描述

9、点击上方功能栏生成、生成解决方案。可以在项目目录X64/debug文件夹里找到生成的DLL。

在这里插入图片描述

10、到此,简单的DLL动态库文件创建完成,转战JAVA如何使用JNA调用动态库。

二、JAVA使用JNA调用DLL动态库。

1、创建简单的java工程,把刚才生成的dll文件放在你的src目录下。

2、选择jdk版本,注意的是jdk位数要和你生成的dll位数相同,64位的用64位jdk,32位的用32位的jdk。

3、导入jna的jar包,我这边用的是jna-4.1.0.jar,下载。。。自己找找吧。

在这里插入图片描述

4、创建一个类,在里面写一个接口继承Library,在里面使用JNA提供的方法,Native.loadLibrary(“路径”,TestDll.class); 在接口里面声明dll的函数,非常简单,不需要写任何的C代码就能调用DLL动态库

在这里插入图片描述

5、调用DLL的函数测试

注意的是这里的输出值都是从dll函数里得到的,java这边并没有复制语句,测试成功

在这里插入图片描述
代码:

import com.sun.jna.Library;
import com.sun.jna.Native;

public class JNA {
public interface TestDll extends Library {
TestDll Instance = (TestDll)Native.loadLibrary("com/df/util/ygt",TestDll.class);	
  	int add(int a, int b);	
	String print();
	}
	
public static void main(String[] args) {
	TestDll testDll=TestDll.Instance;
	int c = testDll.add(3, 4);
	System.out.println("第一个函数的测试:"+c);
		
	String a = testDll.print();
	System.out.println("第二个函数的测试:"+a);
	}
}

三、bug总结

在写demo的时候最常出现的bug无非是以下这些

1、此程序不是正常的32位应用程序

2、找不到指定模块

3、无法加载指定模板

4、找不到指定函数

鄙人不才,这些坑我都踩过,我对这门技术也不是很深,毕竟我是第一次写这C++的DLL动态库,出现这些bug的大部分原因会是在dll的编写过程,jna调用这边难度不大。

解决方案大致也是我上面的细节,一些书写格式,配置等。

在dll编写这边:

1、选择生成的DLL位数选择要对应,X64/X86。

2、头文件函数的声明格式要对,仿照这个格式:

extern “C” _declspec(dllexport)int add(int a, int b);

3、配置属性要对,重要的两点就是上面的,一个是运行库要清空、二是链接库依赖项选否。

在JAVA这边:

1、jdk版本与dll生成位数是否一致,32对32、64对64

2、dll文件的路径,放在src目录下,如果不行的可以尝试放在桌面或者系统的system.32文件夹里,取绝对路径。

最后还有一点,DLL是用C++写的,java跟c的数据类型还是有区别的,我们得对应数据表来对应双方的数据传递。

在这里插入图片描述

四、跨平台、跨语言调用的原则

任何跨平台、跨语言的调用上面,数据传递都是非常敏感的,不通过语言的数据类型不同,对于复杂的数据类型,传递的难度是非常大的,很多失败的原因也是因此。
另外,跨平台、跨语言调用是忌讳大量数据以及复杂数据类型传递的,就以JNA调用DLL动态库为例子,如果两者之间存在大量的数据交换,因为两者是不同的语言平台,所以java在传递数据给dll时,会把数据加载到操作机的物理内存上,dll才能访问到这些数据,但是java的gc管理不了这个内存,无法释放,会造成内存碎片,如果数据庞大,复杂,那么内存溢出的情况是非常容易发生的。这无法避免,所以在涉及到大量数据交换时,要另写一个动态库,编写函数,降低数据量的传输,降低数据的复杂性,尽量使用基本数据类型。
最近公司有在使用jna调用dll的项目,其中包含了大量数据传递,造成内存溢出,jvm调优等等方案都无法解决,最终提出把数据以数据库为第三方的方式进行传递,我们知道C++是可以直接访问数据库的,DLL是C++写的自然也可以对数据库进行操作,但是这里仍然存在很多的坑,如果不一一踩掉,那么很难实现操作。
可能在处理数据传递方面,还有更好的办法,比如写个C的服务,让c去调用dll,java通过服务传递数据给c,这是一个设想,并未实际去操作过,不过网上关于dll里调用数据库的文献少之又少,可能方案欠妥还是什么,有待考究,下次我会写一个dll里如何操作数据库,jna又如何实现调用dll并且传递数据避免内存溢出的问题。

五、如果文章对你在jna调用dll的经历上有所帮助,三连一下吧,谢谢,渣娃们。

猜你喜欢

转载自blog.csdn.net/GTFaker/article/details/106230880
今日推荐