java调用动态库dll详细示例代码
数据类型对应关系
| Java Type | C Type |
|---|---|
| boolean | int |
| byte | char |
| char | wchar_t |
| short | short |
| double | double |
| float | float |
| String | char* |
JNI 调用 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创建项目 - Visual C++ - Windows 桌面 - 动态链接库(DLL)
新建项目JniDemo
注:选错了工程类型报错(UnsatisfiedLinkError: dll: 此操作仅在应用容器上下文中有效。)拷贝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());
}
}
缺点
如果有一个现有的.dll/.so文件,如果使用JNI技术调用,我们首先需要另外使用C语言写一个.dll/.so共享库,使用SUN规定的数据结构替代C语言的数据结构,调用已有的 dll/so中公布的函数。
然后再在Java中载入这个适配器dll/so,再编写Java native函数作为dll中函数的代理。
Jnative 调用 dll
Jnative是对JNI技术进行了封装,更加方便的让java去调用DLL
JNA调用dll
JNA全称Java Native Access
https://blog.csdn.net/gwd1154978352/article/details/55097376/
JNA中,它提供了一个动态的C语言编写的转发器,可以自动实现Java和C的数据类型映射。不再需要编写C动态链接库。
地址: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没办法直接调用类方法,需要将类方法“取出来”重新封装一遍。同时为了保持类的特性,每个方法增加一个参数,用于传递类对象的引用。
示例
需要定义一个接口,继承自Library 或StdCallLibrary
public interface CLibrary extends Library {
CLibrary INSTANCE = (CLibrary)
Native.loadLibrary((Platform.isWindows() ? "msvcrt" : "c"),
CLibrary.class);
void printf(String format, Object... args);
}
搜索动态链接库路径的顺序是:先从当前类的当前文件夹找,如果没有找到,再在工程当前文件夹下面找win32/win64文件夹,找到后搜索对应的dll文件,如果找不到再到WINDOWS下面去搜索,再找不到就会抛异常了。
JNA模拟结构体
Structure 子类中的公共字段的顺序,必须与C 语言中的结构的顺序一致。否则会报错!
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 类有两个内部接口Structure.ByReference 和Structure.ByValue。这两个接口仅仅是标记,如果一个类实现Structure.ByReference 接口,就表示这个类代表结构体指针。
如果一个类实现Structure.ByValue 接口,就表示这个类代表结构体本身。
JNA模拟复杂结构体
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("杨致远");
// pUserStruct.write();
for(int i=0;i<companyStruct2.count;i++){
companyStruct2.users[i]=pUserStruct;
}
TestDll1.INSTANCE.sayCompany2(companyStruct2);
考察JNI 技术,我们发现Java 调用原生函数时,会把传递给原生函数的Java 数据固定在内存中,这样原生函数才可以访问这些Java 数据。对于没有固定住的Java 对象,GC 可以删除它,也可以移动它在内存中的位置,以使堆上的内存连续。如果原生函数访问没有被固定住的Java 对象,就会导致调用失败。
固定住哪些java 对象,是JVM 根据原生函数调用自动判断的。而上面的CompanyStruct2结构体中的一个字段是UserStruct 对象指针的数组,因此,JVM 在执行时只是固定住了CompanyStruct2 对象的内存,而没有固定住users 字段引用的UserStruct 数组。因此,造成了错误。
我们需要把users 字段引用的UserStruct 数组的所有成员也全部固定住,禁止GC 移动或者删除。
如果我们执行了pUserStruct.write();这段代码,那么就可以成功执行上述代码。
Structure 类的write()方法会把结构体的所有字段固定住,使原生函数可以访问。
总结
到此这篇关于java调用动态库dll的文章就介绍到这了,更多相关java调用动态库dll内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
SpringData JPA中查询接口Repository的使用
本文主要介绍了SpringData JPA中查询接口Repository的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧2023-07-07
使用CI/CD工具Github Action发布jar到Maven中央仓库的详细介绍
今天通过对Github Action的简单使用来介绍了CI/CD的作用,这个技术体系是项目集成交付的趋势,也是面试中的一个亮点技能。 而且这种方式可以实现“一次配置,随时随地集成部署”,感兴趣的朋友一起看看吧2021-07-07
如何禁用IntelliJ IDEA的LightEdit模式(推荐)
这篇文章主要介绍了如何禁用IntelliJ IDEA的LightEdit模式,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下2020-04-04


最新评论