数据类型对应关系
| 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内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论