JNA加载DLL及在jar中的运用

需求相关

  • 需求
    使用JNI方式加载DLL,并封装为SDK供他人使用。
  • 方法
    • 原生JNI方式
    • 调用JNA框架
  1. 原生JNI方式适合自行定制的dll,也就是在拥有dll源码的情况下,可操作性比较强;不过如果方法太多以及涉及结构体之类,需要自己手写许多类型转换
  2. JNA通用于各种场景,在类型转换上比较省功夫,懒人必备

此次目标是一个只有头文件的windows32位dll文件,所以选择使用JNA。

注意,因为是win32平台,所以全程使用的32位的JDK,这里用的jdk_1.8.152_x86

DLL相关

当然为了踩坑有条件的话还是自已创建dll来尝试。所以我选择祭出宇宙第一IDE,用牛刀来杀个鸡—生成一个简单的dll文件
头文件:

#pragma once

#define DLL_API _declspec(dllexport) 

extern "C" {
	DLL_API int getVersion(void);
	DLL_API int add(int a, int b);
}

源码:

#include "pch.h"
#include "soft.h"

int getVersion(void) {
	return 2;
}

int add(int a, int b) {
	return a + b;
}

接下来自然是生成DLL。这里有一些配置要注意:

  1. 使用Release/win32来生成解决方案
  2. 选择“在静态库中使用MFC”

如图配置即可:
MFC
如果不这样配置,将会出现Can’t find dependent libraries问题,大概意思是当前的dll要依赖另外的dll,但在当前环境下找不到。具体依赖可以使用dependencywalker进行检测查询,出现下图类似的情况就说明缺少依赖:
DLL依赖检测工具
配置好了,就可以捡到一个新鲜的dll文件。

好,现在我们模拟一种情况—源码丢失了。这样就变成了最开始提到的需求。

JNA相关

JNA是一个成熟的框架,当然是有组织的。各路好汉都可以在github上得到最新版本,传送门在此:
jna
当前最新版本是5.5.0。这里也直接转述其jar的maven地址:
jna-5.5.0
jna-platform-5.5.0
两个包,前者相当于内核包,后者类似于插件包。一般情况下下载前者即可。

然后…

你会发现这个jar包真大。
jna-jar大小
当前是1.4M大小,而我们在上一步生成的Dll文件的大小:
dll大小
牛刀杀鸡 X 2。

此事万不能忍,得削它。

通过打开jar包我们可以看到:
jar包内容
这一看就是为各种环境准备的兼容包,这里我们只需要win32位的,得想办法得到源码并删减它,再重新生成一个jna的jar包出来。
然而在maven上提供的jna-5.5.0-sources.jar源码包中并没有这些平台相关的东西,所以只能手动合一下。好在这里平台相关的兼容包中并不是class文件,而是各类so文件、dll文件、jnilib文件等。所以可以直接合。

具体操作:

  1. 把jna-5.5.0-sources.jar源码包中的代码全部复制出来
  2. 把jna-5.5.0.jar目录下的所需要的环境兼容包复制出来
  3. 生成jar包

在IDEA中大概是这样:
IDEA-jna
这里只需要win32-x86的环境支持,所以只复制了这一个环境的兼容包,可以看到,这里只需要添加jnidispatch.dll文件即可。
随后配置一下artifacts,快捷生成jar包:
jna-slim-artifacts
如此就捡到了一个大小288KB的仅支持win32-x86的jna包:
jna-slim大小
虽然还是有点大,但先凑合用吧。

使用JNA加载DLL

先在外部工程中测试一下。首先将之前生成的SimpleDll.dll文件放到工程根目录下,然后只需要写一个继承Libary的接口,加载dll以及声明dll中的方法:

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

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

	int getVersion();

	int add(int a, int b);
}

最后调用即可:

public class Main {

    public static void main(String[] args) {
        System.out.println(ISimpleDll.INSTANCE.getVersion());
        System.out.println(ISimpleDll.INSTANCE.add(1,2));
    }
}

输出:

2
3

其目录结构如下:
目录结构
至此说明JNA包和DLL文件均为正常。

在jar中使用JNA加载DLL

在jar包中使用JNA加载dll,最大的问题其实是路径问题,也就是怎样让jna在jar包仍然可以找到相应的dll文件

将SimpleDll文件放到Libary Module的根目录,并打出jar包:
somesdk内容
然后使用的时候提示:

Exception in thread "main" java.lang.UnsatisfiedLinkError: Unable to load library 'SimpleDll':
找不到指定的模块。

找不到指定的模块。

找不到指定的模块。

Can't obtain InputStream for win32-x86/SimpleDll.dll
	at com.sun.jna.NativeLibrary.loadLibrary(NativeLibrary.java:302)
	at com.sun.jna.NativeLibrary.getInstance(NativeLibrary.java:455)
	at com.sun.jna.Library$Handler.<init>(Library.java:192)
	at com.sun.jna.Native.loadLibrary(Native.java:646)
	at com.sun.jna.Native.loadLibrary(Native.java:630)
	at SomeSDK.<init>(SomeSDK.java:16)
	at SomeSDK.<init>(SomeSDK.java:3)
	at SomeSDK$Holder.<clinit>(SomeSDK.java:6)
	at SomeSDK.getInstance(SomeSDK.java:10)
	at Main.main(Main.java:4)

似乎是想win32-x86目录下去找这个dll文件,那简单—

改一下artifacts的配置,手动添加一个目录就行:
again
果然就可以了。
新版的JNA似乎在对路径上的查找上作了一些改变。

最后,所有JAVA代码上传到github上了。
JnaDemo

最后的最后,再次强调,本次调试全程使用的win32的dll,以及32位的JDK。

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值