欢迎光临
我们一直在努力

java调用动态库dll

文章目录

  • 数据类型对应关系
  • JNI 调用 dll
    • 示例
    • 缺点
  • Jnative 调用 dll
  • JNA调用dll
    • 示例
    • JNA模拟结构体
    • JNA模拟复杂结构体

C++编写的程序如果用于windows使用则编译为xxx.dll文件,如果是Linux使用则编译为libxxx.so文件。

数据类型对应关系

Java TypeC 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()方法会把结构体的所有字段固定住,使原生函数可以访问。

    赞(0)
    未经允许不得转载:171主机测评 » java调用动态库dll
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址