java調(diào)用動態(tài)庫dll詳細(xì)示例代碼
數(shù)據(jù)類型對應(yīng)關(guān)系
| Java Type | C Type |
|---|---|
| boolean | int |
| byte | char |
| char | wchar_t |
| short | short |
| double | double |
| float | float |
| String | char* |
JNI 調(diào)用 dll
示例
- 包jni中新建java文件,JniDemo.java
package jni;
public class JniDemo {
public native static void set(int i);
public native static int get();
}
- 使用javah命令生成頭文件(jni_JniDemo.h)
javah jni.JniDemo
vs2017創(chuàng)建項目 - Visual C++ - Windows 桌面 - 動態(tài)鏈接庫(DLL)
新建項目JniDemo
注:選錯了工程類型報錯(UnsatisfiedLinkError: dll: 此操作僅在應(yīng)用容器上下文中有效。)拷貝jni_JniDemo.h,jni.h,jni_md.h至vs工程cpp文件目錄下
jni.h在D:\Java\jdk1.8.0_111\include\jni.h
jni_md.h在D:\Java\jdk1.8.0_111\include\win32\jni_md.h編輯文件
修改文件jni_JniDemo.h:
#include <jni.h> 改為 #include "jni.h"
修改文件JniDemo.cpp:
#include "stdafx.h"
#include "jni_JniDemo.h"
int number = 0;
JNIEXPORT void JNICALL Java_jni_JniDemo_set
(JNIEnv *, jclass, jint i)
{
number = i;
}
JNIEXPORT jint JNICALL Java_jni_JniDemo_get
(JNIEnv *, jclass)
{
return number;
}
生成64位dll庫
文件位置:D:\vsrepos\JniDemo\Release\JniDemo.dll把64位dll庫拷貝至Java JniDemo項目根目錄下,修改JniDemo.java
public class JniDemo {
static{
System.loadLibrary("JniDemo");
}
public native static void set(int i);
public native static int get();
public static void main(String[] args) {
// System.out.println(System.getProperty("java.library.path"));
set(100);
System.out.println(get());
}
}
缺點
如果有一個現(xiàn)有的.dll/.so文件,如果使用JNI技術(shù)調(diào)用,我們首先需要另外使用C語言寫一個.dll/.so共享庫,使用SUN規(guī)定的數(shù)據(jù)結(jié)構(gòu)替代C語言的數(shù)據(jù)結(jié)構(gòu),調(diào)用已有的 dll/so中公布的函數(shù)。
然后再在Java中載入這個適配器dll/so,再編寫Java native函數(shù)作為dll中函數(shù)的代理。
Jnative 調(diào)用 dll
Jnative是對JNI技術(shù)進(jìn)行了封裝,更加方便的讓java去調(diào)用DLL
JNA調(diào)用dll
JNA全稱Java Native Access
https://blog.csdn.net/gwd1154978352/article/details/55097376/
JNA中,它提供了一個動態(tài)的C語言編寫的轉(zhuǎn)發(fā)器,可以自動實現(xiàn)Java和C的數(shù)據(jù)類型映射。不再需要編寫C動態(tài)鏈接庫。
地址:https://github.com/java-native-access/jna
<!-- https://mvnrepository.com/artifact/net.java.dev.jna/jna -->
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>5.3.1</version>
</dependency
JNA沒辦法直接調(diào)用類方法,需要將類方法“取出來”重新封裝一遍。同時為了保持類的特性,每個方法增加一個參數(shù),用于傳遞類對象的引用。
示例
需要定義一個接口,繼承自Library 或StdCallLibrary
public interface CLibrary extends Library {
CLibrary INSTANCE = (CLibrary)
Native.loadLibrary((Platform.isWindows() ? "msvcrt" : "c"),
CLibrary.class);
void printf(String format, Object... args);
}
搜索動態(tài)鏈接庫路徑的順序是:先從當(dāng)前類的當(dāng)前文件夾找,如果沒有找到,再在工程當(dāng)前文件夾下面找win32/win64文件夾,找到后搜索對應(yīng)的dll文件,如果找不到再到WINDOWS下面去搜索,再找不到就會拋異常了。
JNA模擬結(jié)構(gòu)體
Structure 子類中的公共字段的順序,必須與C 語言中的結(jié)構(gòu)的順序一致。否則會報錯!
c:
struct UserStruct{
long id;
wchar_t* name;
int age;
};
jna:
public static class UserStruct extends Structure{
public NativeLong id;
public WString name;
public int age;
public static class ByReference extends UserStruct
implements Structure.ByReference { }
public static class ByValue extends UserStruct implements
Structure.ByValue
{ }
}
public void sayUser(UserStruct.ByReference struct);
Structure 類有兩個內(nèi)部接口Structure.ByReference 和Structure.ByValue。這兩個接口僅僅是標(biāo)記,如果一個類實現(xiàn)Structure.ByReference 接口,就表示這個類代表結(jié)構(gòu)體指針。
如果一個類實現(xiàn)Structure.ByValue 接口,就表示這個類代表結(jié)構(gòu)體本身。
JNA模擬復(fù)雜結(jié)構(gòu)體
c:
struct CompanyStruct{
long id;
wchar_t* name;
UserStruct users[100];
int count;
};
jna:
public static class CompanyStruct extends Structure{
public NativeLong id;
public WString name;
public UserStruct.ByValue[] users=new UserStruct.ByValue[100];
public int count;
}
測試代碼:
CompanyStruct2.ByReference companyStruct2=new CompanyStruct2.ByReference();
companyStruct2.id=new NativeLong(2);
companyStruct2.name=new WString("Yahoo");
companyStruct2.count=10;
UserStruct.ByReference pUserStruct=new
UserStruct.ByReference();
pUserStruct.id=new NativeLong(90);
pUserStruct.age=99;
pUserStruct.name=new WString("楊致遠(yuǎn)");
// pUserStruct.write();
for(int i=0;i<companyStruct2.count;i++){
companyStruct2.users[i]=pUserStruct;
}
TestDll1.INSTANCE.sayCompany2(companyStruct2);
考察JNI 技術(shù),我們發(fā)現(xiàn)Java 調(diào)用原生函數(shù)時,會把傳遞給原生函數(shù)的Java 數(shù)據(jù)固定在內(nèi)存中,這樣原生函數(shù)才可以訪問這些Java 數(shù)據(jù)。對于沒有固定住的Java 對象,GC 可以刪除它,也可以移動它在內(nèi)存中的位置,以使堆上的內(nèi)存連續(xù)。如果原生函數(shù)訪問沒有被固定住的Java 對象,就會導(dǎo)致調(diào)用失敗。
固定住哪些java 對象,是JVM 根據(jù)原生函數(shù)調(diào)用自動判斷的。而上面的CompanyStruct2結(jié)構(gòu)體中的一個字段是UserStruct 對象指針的數(shù)組,因此,JVM 在執(zhí)行時只是固定住了CompanyStruct2 對象的內(nèi)存,而沒有固定住users 字段引用的UserStruct 數(shù)組。因此,造成了錯誤。
我們需要把users 字段引用的UserStruct 數(shù)組的所有成員也全部固定住,禁止GC 移動或者刪除。
如果我們執(zhí)行了pUserStruct.write();這段代碼,那么就可以成功執(zhí)行上述代碼。
Structure 類的write()方法會把結(jié)構(gòu)體的所有字段固定住,使原生函數(shù)可以訪問。
總結(jié)
到此這篇關(guān)于java調(diào)用動態(tài)庫dll的文章就介紹到這了,更多相關(guān)java調(diào)用動態(tài)庫dll內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中遍歷Map的多種方法示例及優(yōu)缺點總結(jié)
在java中遍歷Map有不少的方法,下面這篇文章主要給大家介紹了關(guān)于Java中遍歷Map的多種方法,以及各種方法的優(yōu)缺點總結(jié),文中通過示例代碼介紹的非常詳細(xì),對大家具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起看看吧。2017-07-07
SpringData JPA中查詢接口Repository的使用
本文主要介紹了SpringData JPA中查詢接口Repository的使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07
Springmvc發(fā)送json數(shù)據(jù)轉(zhuǎn)Java對象接收
這篇文章主要介紹了Springmvc發(fā)送json數(shù)據(jù)轉(zhuǎn)Java對象接收,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-10-10
idea中安裝VisualVM監(jiān)控jvm的圖文教程
這篇文章主要介紹了idea中安裝VisualVM監(jiān)控jvm的教程,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-09-09
使用CI/CD工具Github Action發(fā)布jar到Maven中央倉庫的詳細(xì)介紹
今天通過對Github Action的簡單使用來介紹了CI/CD的作用,這個技術(shù)體系是項目集成交付的趨勢,也是面試中的一個亮點技能。 而且這種方式可以實現(xiàn)“一次配置,隨時隨地集成部署”,感興趣的朋友一起看看吧2021-07-07
如何禁用IntelliJ IDEA的LightEdit模式(推薦)
這篇文章主要介紹了如何禁用IntelliJ IDEA的LightEdit模式,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-04-04

