文章目錄
-
-
- 需求相關
- DLL相關
- JNA相關
- 使用JNA加載DLL
-
- 在jar中使用JNA加載DLL
-
需求相關
-
需求
使用JNI方式加載DLL,并封裝為SDK供他人使用。
- 方法
- 原生JNI方式
- 調用JNA架構
- 原生JNI方式适合自行定制的dll,也就是在擁有dll源碼的情況下,可操作性比較強;不過如果方法太多以及涉及結構體之類,需要自己手寫許多類型轉換
- 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。這裡有一些配置要注意:
- 使用Release/win32來生成解決方案
- 選擇“在靜态庫中使用MFC”
如圖配置即可:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL90TUNhXQq1EbaNjYzxGWi1GbywEMW1mY1RzRapnTtxkb5ckYplTeMZTTINGMShUYfRHelRHLwEzX39GZhh2css2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xyayFWbyVGdhd3LcV2Zh1Wa9M3clN2byBXLzN3btg3Pn5GcuYDN3MzMyUTM1AjMwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
如果不這樣配置,将會出現Can’t find dependent libraries問題,大概意思是目前的dll要依賴另外的dll,但在目前環境下找不到。具體依賴可以使用dependencywalker進行檢測查詢,出現下圖類似的情況就說明缺少依賴:
配置好了,就可以撿到一個新鮮的dll檔案。
好,現在我們模拟一種情況—源碼丢失了。這樣就變成了最開始提到的需求。
JNA相關
JNA是一個成熟的架構,當然是有組織的。各路好漢都可以在github上得到最新版本,傳送門在此:
jna
目前最新版本是5.5.0。這裡也直接轉述其jar的maven位址:
jna-5.5.0
jna-platform-5.5.0
兩個包,前者相當于核心包,後者類似于插件包。一般情況下下載下傳前者即可。
然後…
你會發現這個jar包真大。
目前是1.4M大小,而我們在上一步生成的Dll檔案的大小:
牛刀殺雞 X 2。
此事萬不能忍,得削它。
通過打開jar包我們可以看到:
這一看就是為各種環境準備的相容包,這裡我們隻需要win32位的,得想辦法得到源碼并删減它,再重新生成一個jna的jar包出來。
然而在maven上提供的jna-5.5.0-sources.jar源碼包中并沒有這些平台相關的東西,是以隻能手動合一下。好在這裡平台相關的相容包中并不是class檔案,而是各類so檔案、dll檔案、jnilib檔案等。是以可以直接合。
具體操作:
- 把jna-5.5.0-sources.jar源碼包中的代碼全部複制出來
- 把jna-5.5.0.jar目錄下的所需要的環境相容包複制出來
- 生成jar包
在IDEA中大概是這樣:
這裡隻需要win32-x86的環境支援,是以隻複制了這一個環境的相容包,可以看到,這裡隻需要添加jnidispatch.dll檔案即可。
随後配置一下artifacts,快捷生成jar包:
如此就撿到了一個大小288KB的僅支援win32-x86的jna包:
雖然還是有點大,但先湊合用吧。
使用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包:
然後使用的時候提示:
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的配置,手動添加一個目錄就行:
果然就可以了。
新版的JNA似乎在對路徑上的查找上作了一些改變。
最後,所有JAVA代碼上傳到github上了。
JnaDemo
最後的最後,再次強調,本次調試全程使用的win32的dll,以及32位的JDK。