java调用dll_java调用dll文件 - CSDN
精华内容
参与话题
  • java调用dll

    2020-01-14 10:01:07
    在网上找了很多jna调用dll的用例,很多博客连dll代码都不发布出来,仅仅是写一段java代码。 因此,这里我手把手教大家如何开发属于自己的demo。包括64位dll的开发和java代码的开发示例,因为现在大部分都是64位的...

    前言:

    在网上找了很多jna调用dll的用例,很多博客连dll代码都不发布出来,仅仅是写一段java代码。

    因此,这里我手把手教大家如何开发属于自己的demo。包括64位dll的开发和java代码的开发示例,因为现在大部分都是64位的jdk了吧?

    遇到的坑:32位dll只能是32位jdk去调用,64位dll只能是64位dll去调用。这个你们不要想着去逆天,如果发现调用不了的情况请检查一下dll的位数或者jdk的位数

    一、开发dll

    1.VS2010下载

    可以参考一下这篇经验

    https://jingyan.baidu.com/article/fd8044fa0b46085031137ace.html

    本着送佛送到西的原则:下载链接 密码是:6ukc,当然,这不是我的云盘。

     

    2.dll的开发

    ①:VS新建项目

    项目名称自定义: 

     点击空项目,完成

    名称就英文吧,别起花里胡哨的中文名字 

    将这段代码复制进去:

    这里要注意的是:java的String和cpp的String不一样的,其对应的是char*,如果要用cpp的string不是乱码就是调用失败,不信你试试。具体的参数字段对应表可以参考一下这篇博文,虽然这货写的不怎么样,但是字段映射表已经是比较清楚的了

    https://blog.csdn.net/abc6368765/article/details/77842066

    #include <windows.h> 
    #include <iostream>
    #include <string>
    using std::string;
    using std::cin;
    using std::cout;
    using std::endl; 
     
    #define MYLIBAPI extern "C" __declspec( dllexport ) 
    
    //这的参数是必须的,也可以定义为.c头文件
    MYLIBAPI double add(double a,double b);
    MYLIBAPI double mul(double a,double b);
    MYLIBAPI char * getString(char* a);
     
    double add(double a,double b){  
        return a + b;  
    }
    
    double mul(double a,double b){
    	return a*b;
    }
    //定义了一个返回java String类型的参数
    char * getString(char* a){
    	char* b ="this is test";
    	return strcat(a,b);
    }

    ②:dll生成

    这里不要开小差,不要想当然,这是最关键的一步。

    1.这里变成Release,点击配置管理器配置x64版本,这样生成的dll就是x64版本的,这点非常重要

     

    配置完成以后右击项目点击生成

    不出意外的话肯定能生成,照着我的代码写都没成功,你是有多非酋?

    dll位置:右击项目

    这里要注意的是是在上级目录!不要想当然打开的项目位置然后直接就去x64去找了,根本没用!里面没有dll

    是在上级目录,上级目录 的x64位置

    好了,dll开发就大功完成了。什么?你不懂dll代码是什么意思?老兄你java水平过关了吗? 

     二、java代码开发

    1.导入maven依赖的包

    也许导入了这些包,有的牛角尖者就嚷嚷了,啊我导入这一个包就ok了,你凭什么要我多导入一个包?!

    这里我想说的是:这点空间都没有,你是活在上个世纪的开发者吗?

     <!-- 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>
            <!-- https://mvnrepository.com/artifact/net.java.dev.jna/jna-platform -->
            <dependency>
                <groupId>net.java.dev.jna</groupId>
                <artifactId>jna-platform</artifactId>
                <version>5.3.1</version>
            </dependency>

    2.编写代码

    ①dll位置

    我个人就放在这个lib包下面,这样导入这个包的时候可以写绝对路径也可以写相对路径,至于idea怎么查看绝对路径和相对路径或者这两个概念没有弄清楚的,请移步java基础教学

    ②:java代码

    这里我写的就是相对路径,至于有的人说不用写dll后缀,呵呵

    定义的接口方法名称需要和dll中的方法名称一致。

    package jni;
    
    import com.sun.jna.Library;
    import com.sun.jna.Native;
    
    /**
     * @author ilongsay
     * @date: Create in 2019/11/27 16:50
     * @description: don't bb,show you code
     */
    public class JnaTest2 {
        public interface TestProject extends Library {
            TestProject INSTANCE = (TestProject) Native.load("src/main/lib/testDll.dll",
                    JnaTest2.TestProject.class);
            public double add(double i, double j);
            public double mul(double i, double j);
            public String getString(String a);
    
        }
    
        public static void main(String[] args) {
    
            System.out.println(TestProject.INSTANCE.add(20.11,20.0));
            System.out.println(TestProject.INSTANCE.mul(16.9,20.89));
            System.out.println(TestProject.INSTANCE.getString("我现在正在测试dllgihjb"));
        }
    }
    

     ③:运行代码

    是不是很熟悉的感觉?照着我的写又不会乱码,又能成功!怎样?比网络上的要详细很多吧?或者你找一篇比我还详细的文章给我,看我不打死你。

    到了这一步,就成功了,如果还没成功,请顺着网线过来打我!

    展开全文
  • JAVA调用DLL 超详细代码实战

    万次阅读 2018-05-07 21:35:49
    0 JNA安装0.1 JNA是什么JNA...JNA项目地址:https://jna.dev.java.net/JNA使Java调用原生函数就像.NET上的P/Invoke一样方便、快捷。JNA的功能和P/Invoke类似,但编写方法与P/Invoke截然不同。JNA没有使...

    0  JNA安装

    0.1 JNA是什么

    JNA(JavaNativeAccess)框架是一个开源的Java框架,是SUN公司主导开发的,建立在
    经典的JNI的基础之上的一个框架。
    JNA项目地址:https://jna.dev.java.net/
    JNA使Java调用原生函数就像.NET上的P/Invoke一样方便、快捷。
    JNA的功能和P/Invoke类似,但编写方法与P/Invoke截然不同。JNA没有使用Annotation,
    而是通过编写一般的Java代码来实现。
    P/Invoke是.NET平台的机制。而JNA是Java平台上的一个开源类库,和其他类库没有
    什么区别。只需要在classpath下加入jna.jar包,就可以使用JNA。
    JNA使Java平台可以方便地调用原生函数,这大大扩展了Java平台的整合能力。

    0.2 加入

    在eclipse 中将文章最后的JNA下载中的zip解压放到一个记得住的文件夹然后在工程的JAVA Build Path中加入这两个jar即可



    1 使用步骤

    1.1 编写DLL

    这里我们使用 code::blocks编写dll

    首先新建一个DLL工程


    建立完毕后我们可以看到这样的结构

    其中有一个h文件和一个cpp文件

    依葫芦画瓢 改写这两个文件就可以了,我们写两个函数,一个加一个阶乘

    main.h 改写成

    1. #ifndef __MAIN_H__  
    2. #define __MAIN_H__  
    3.   
    4. #include <windows.h>  
    5.   
    6. /*  To use this exported function of dll, include this header 
    7.  *  in your project. 
    8.  */  
    9.   
    10. #ifdef BUILD_DLL  
    11.     #define DLL_EXPORT __declspec(dllexport)  
    12. #else  
    13.     #define DLL_EXPORT __declspec(dllimport)  
    14. #endif  
    15.   
    16.   
    17. #ifdef __cplusplus  
    18. extern "C"  
    19. {  
    20. #endif  
    21.   
    22. int add(int a,int b);  
    23. int factorial(int n);  
    24.   
    25. #ifdef __cplusplus  
    26. }  
    27. #endif  
    28.   
    29. #endif // __MAIN_H__  

    main.cpp改写成

    1. #include "main.h"  
    2.   
    3. int add(int a,int b){  
    4.     return a + b;  
    5. }  
    6. int factorial(int n){  
    7.     int i;  
    8.     int r = 1;  
    9.     for(i=1;i<n+1;i++)  
    10.         r = r*i;  
    11.     return r;  
    12. }  
    13.   
    14. BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)  
    15. {  
    16.     switch (fdwReason)  
    17.     {  
    18.         case DLL_PROCESS_ATTACH:  
    19.             // attach to process  
    20.             // return FALSE to fail DLL load  
    21.             break;  
    22.   
    23.         case DLL_PROCESS_DETACH:  
    24.             // detach from process  
    25.             break;  
    26.   
    27.         case DLL_THREAD_ATTACH:  
    28.             // attach to thread  
    29.             break;  
    30.   
    31.         case DLL_THREAD_DETACH:  
    32.             // detach from thread  
    33.             break;  
    34.     }  
    35.     return TRUE; // succesful  
    36. }  

    其中DLLMain是动态链接库的入口函数我们不用管它,点击编译按钮


    这样就生成了DLL文件了,我们在项目文件夹 bin文件夹下的Debug中可以找到编译成了DLL文件,将这个文件拷贝到需要使用DLL的JAVA工程路径下



    这样DLL文件就准备好了

    1.2 编写接口类

    新建一个类,实现Library接口

    [java] view plain copy
    1. package implementation;  
    2.   
    3. import com.sun.jna.Library;  
    4. import com.sun.jna.Native;  
    5.   
    6. public interface JNATestDll extends Library {  
    7.     JNATestDll instanceDll  = (JNATestDll)Native.loadLibrary("JNATestDLL",JNATestDll.class);  
    8.     public int add(int a,int b);  
    9.     public int factorial(int n);  
    10. }  

    这样就可以方便的调用这一DLL了

    编写一个GUI类用于展示结果

    [java] view plain copy
    1. package gui;  
    2.   
    3. import implementation.JNATestDll;  
    4.   
    5. public class GUI {  
    6.   
    7.     private JFrame frame;  
    8.     private JTextField tfb;  
    9.     private JTextField tfa;  
    10.     private JTextField tfadd;  
    11.     private JTextField tfn;  
    12.     private JTextField tffactorial;  
    13.   
    14.     /** 
    15.      * Launch the application. 
    16.      */  
    17.     public static void main(String[] args) {  
    18.         EventQueue.invokeLater(new Runnable() {  
    19.             public void run() {  
    20.                 try {  
    21.                     GUI window = new GUI();  
    22.                     window.frame.setVisible(true);  
    23.                 } catch (Exception e) {  
    24.                     e.printStackTrace();  
    25.                 }  
    26.             }  
    27.         });  
    28.     }  
    29.   
    30.     /** 
    31.      * Create the application. 
    32.      */  
    33.     public GUI() {  
    34.         initialize();  
    35.     }  
    36.   
    37.     /** 
    38.      * Initialize the contents of the frame. 
    39.      */  
    40.     private void initialize() {  
    41.         frame = new JFrame();  
    42.         frame.setBounds(100100450300);  
    43.         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  
    44.         frame.getContentPane().setLayout(null);  
    45.           
    46.         JButton bntcompute = new JButton("compute");  
    47.         bntcompute.addActionListener(new ActionListener() {  
    48.             public void actionPerformed(ActionEvent e) {  
    49.                 tfadd.setText(""+JNATestDll.instanceDll.add(Integer.valueOf(tfb.getText()), Integer.valueOf(tfa.getText())));  
    50.                 tffactorial.setText(""+JNATestDll.instanceDll.factorial(Integer.valueOf(tfn.getText())));  
    51.             }  
    52.         });  
    53.         bntcompute.setBounds(2861809323);  
    54.         frame.getContentPane().add(bntcompute);  
    55.           
    56.         tfb = new JTextField();  
    57.         tfb.setText("3");  
    58.         tfb.setBounds(1711116621);  
    59.         frame.getContentPane().add(tfb);  
    60.         tfb.setColumns(10);  
    61.           
    62.         tfa = new JTextField();  
    63.         tfa.setText("2");  
    64.         tfa.setBounds(741116621);  
    65.         frame.getContentPane().add(tfa);  
    66.         tfa.setColumns(10);  
    67.           
    68.         tfadd = new JTextField();  
    69.         tfadd.setEditable(false);  
    70.         tfadd.setBounds(2861116621);  
    71.         frame.getContentPane().add(tfadd);  
    72.         tfadd.setColumns(10);  
    73.           
    74.         tfn = new JTextField();  
    75.         tfn.setText("10");  
    76.         tfn.setBounds(741426621);  
    77.         frame.getContentPane().add(tfn);  
    78.         tfn.setColumns(10);  
    79.           
    80.         tffactorial = new JTextField();  
    81.         tffactorial.setEditable(false);  
    82.         tffactorial.setBounds(2861426621);  
    83.         frame.getContentPane().add(tffactorial);  
    84.         tffactorial.setColumns(10);  
    85.     }  
    86. }  

    OK一切就绪!

    2 测试

    运行程序


    我们可以看到 DLL已经成功的工作了

    相信大家已经学会如何用了吧!

    3 参考文献

    文档下载:链接:https://pan.baidu.com/s/18FL6DcH9Rl2wpaEQZ2AF_g 密码:4eok

    转载至https://blog.csdn.net/gcangle/article/details/8504629

    展开全文
  • java调用dll方法详解

    万次阅读 2018-09-09 23:29:47
    初学java调用dll库时,经常出现无法加载库、找不到方法等错误(UnsatisfiedLinkError等)。本文对常见的问题进行详细的分析,给出较为完整的解决方案。 正文: 在java中写一个native方法,实现对dll的调用,一般...

    前言:

    初学java调用dll库时,经常出现无法加载库、找不到方法等错误(UnsatisfiedLinkError等)。本文对常见的问题进行详细的分析,给出较为完整的解决方案。

    正文:

    在java中写一个native方法,实现对dll的调用,一般过程如下:

    public class Native {
    	static native void say(String src);
    	static{
    		System.loadLibrary("libname");
    	}
    }

    然后,用javah 命令生成头文件,得到:

    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include <jni.h>
    /* Header for class Native */
    
    #ifndef _Included_Native
    #define _Included_Native
    #ifdef __cplusplus
    extern "C" {
    #endif
    /*
     * Class:     Native
     * Method:    say
     * Signature: (Ljava/lang/String;)V
     */
    JNIEXPORT void JNICALL Java_Native_say
      (JNIEnv *, jclass, jstring);
    
    #ifdef __cplusplus
    }
    #endif
    #endif
    

    创建C项目(Eclipse平台),将上述的头文件拷贝过来,再写一个.c或者.cpp的文件来实现原生方法

    #include "native.h"
    JNIEXPORT void JNICALL Java_Native_say(JNIEnv* env, jclass clz, jstring src){
    	//....
    }

    build,得到dll文件,改成name.dll,放到java项目下即可。

    经过以上方法,运气好的话,可能一次就成了。但也常见以下错误,我将其分为3类:

    A类错误,找不到/无法加载dll文件:

    0.java.lang.UnsatisfiedLinkError: no XXX  in java.library.path

    1.java.lang.UnsatisfiedLinkError: Can't load library

    2.UnsatisfiedLinkError: XXXXXXX 不是有效的 Win32 应用程序。

    B类错误,在所有成功加载的dll文件中,找不到要调用的方法:

    0.java.lang.UnsatisfiedLinkError:  Native.say(Ljava/lang/String;)V

    C类错误,C.C/C++编译器找不到/无法解析jni.h头文件:

    0.#include <jni.h>这里提示找不到头文件,以及方法声明那里提示语法错误。

    D类错误,jvm版本与dll冲突:

    0.ava.lang.UnsatisfiedLinkError:Can't load IA 32-bit .dll on a AMD 64-bit platform。这类错误好理解,就是32位jvm用了64位dll,或者反过来。也比较容易解决,通常换一个对应版本的dll就可以;或者换jdk版本,但比较麻烦。

    以上4类问题,D类由于解决起来较为简单,不再讲解。

    C类:C.C/C++编译器找不到/无法解析jni.h头文件

    这类错误不属于我们代码编写问题,是编译平台的问题。只要能让编译平台知道去哪里找这个头文件就可以。eclipse的话,如果以mingw为C编译器,比较好的解决办法就是,把jdk目录下,include/jni.h和include/win32/jni_md.h放到mingw文件夹下,mingw版本号那个文件夹里的include中,比如我的目录是:D:\Cbianyi\mingw64\x86_64-w64-mingw32\include。如果是用VS6,可以在下图的位置添加jdk的上述目录:

    如果是VS2017,设置include目录即可。个人推荐写C代码还是用VS的好,VS6也比eclipse要方便。

    A类:dll寻找/加载错误

    我们load一个dll,实际包括寻找和加载两个过程,其中寻找dll这一过程是由java层完成的;加载是jvm完成的。

    A.0和A.1 寻找dll:一般都是出在"找"这个环节。

    寻找-加载dll有两种方法:常见的方法是System.loadLibrary("libname"),参数为dll文件名,不带后缀(下文简称sys.loadlib)。错误的根源一般在此方法。另外一种是用System.load("filename"),参数为dll的绝对路径,带后缀(下文简称sys.load)。如果用这种方法,基本不会错,除非路径写错了或者dll文件自身有问题。这两个方法实际上最终都要通过绝对路径来寻找dll,只不过对于sys.loadlib,java会将这个libname补全为filename,再去找。我们看一下源码。

    这两个方法交汇于ClassLoader. loadLibrary(Class<?> fromClass, String name,boolean isAbsolute)(下文简称loader.load)。当使用sys.load方法时,传到此方法的isAbsolute参数为true,有如下源码:

    static void loadLibrary(Class<?> fromClass, String name,
                                boolean isAbsolute) {
          //...
            if (isAbsolute) {
                if (loadLibrary0(fromClass, new File(name))) {
                    return;
                }
                throw new UnsatisfiedLinkError("Can't load library: " + name);
            }
            //....
        }

    这说明,对于给出绝对路径的sys.load,java将直接去找这个路径,如果没有,就给出UnsatisfiedLinkError("Can't load library: " + name)。

    而如果用的是sys.loadlib,那么isAbsolute为false,要先补全绝对路径。补全的过程比较繁琐,与我们最直接相关的为:

     static void loadLibrary(Class<?> fromClass, String name,
                                boolean isAbsolute) {
           
           //....
             for (int i = 0 ; i < sys_paths.length ; i++) {
                File libfile = new File(sys_paths[i], System.mapLibraryName(name));
                if (loadLibrary0(fromClass, libfile)) {
                    return;
                }
                libfile = ClassLoaderHelper.mapAlternativeName(libfile);
                if (libfile != null && loadLibrary0(fromClass, libfile)) {
                    return;
                }
            }
            if (loader != null) {
                for (int i = 0 ; i < usr_paths.length ; i++) {
                    File libfile = new File(usr_paths[i],
                                            System.mapLibraryName(name));
                    if (loadLibrary0(fromClass, libfile)) {
                        return;
                    }
                    libfile = ClassLoaderHelper.mapAlternativeName(libfile);
                    if (libfile != null && loadLibrary0(fromClass, libfile)) {
                        return;
                    }
                }
            }
            throw new UnsatisfiedLinkError("no " + name + " in java.library.path");
           //....
        }

    也就是说,补全的路径为sys_path和use_paths这两个数组里的内容。如果找不到的话,就会提示 UnsatisfiedLinkError("no " + name + " in java.library.path")这个错误。如果我们不做其他设置,这两个路径分别代表了jre和系统环境变量。显而易见,如果调用这些系统外的dll,可以把写好的dll放到这些环境变量中。或者,可以指定一个java.library.path目录。当有指定这一目录时,上述代码中usr_path将仅有一个该目录,而不是系统环境变量。指定的方式有很多种。但是把自己写的dll放到环境变量(或者设置成环境变量),以及设置 libpath的方法,随着项目的移动、变更和发布,仍然会发生找不到dll的情况。

    我给出的解决方案为,不使用sys.loadlib的方法,而是直接给出“绝对路径”。同样我们不能写死一个路径。实际上,load一个dll和inputstream一个file,十分类似,都是先去找文件,再读到内存中。因此,把dll文件看成一个普通的资源文件即可,相信一个程序员可以有多种方式定位一个项目下的文件,无论项目怎么移动和发布。我用的方法是,先得到类所在的路径,然后根据类路径和dll路径的相对关系拼接的。这样一来,无论怎么移动项目,发布,都不会有问题。如下,我将要加载的B32.dll,放在与Native同级的lib目录下:

    		String dir=Native.class.getResource("").getPath().substring(1);
    		String s1=dir+"lib/B32.dll";
    		System.load(s1);

    如此,可以保证找到dll。

    A.2加载dll

    无论用相对还是绝对路径去寻找dll,当找到这个dll时,jvm将通过Windows系统的LoadLibrary(下文简称win.load)试图将dll加载到内存中。这一部分脱离java层。为证实这一观点,需要一些C的基础知识。我们用ollydbg做一下调试:

    bp LoadLibraryA加断点后,放开几次后看到:

    这个FileName正是要加载的dll。

    然后在ret处断点,得到B32在内存中的映射位置:

    转到0x8B00000看内存,

    可以确定已经成功将B32.dll映射到内存。

    进一步的,我们可以证明,只要一个文件的名称为B32.dll,jvm就会调用win.load方法,试图加载。新建一个txt空文件,改名B32.dll,仍然按上述方法调试,还是可以得到:

    但是,ret将返回0,因为这不是一个有效/合法的dll文件,程序也将报错:

    可以看到,报错提示的是,不是有效的Win32程序。所以我讲“不是有效/合法的dll文件”是不严谨的。并非一定要用dll作为load的对象;.exe也可以。如果把B32.dll改名为B32.exe,源码System.load给出的路径的后缀名也改为.exe,一样是可以成功调用的。

    额外的,我们再验证一下System.loadlibrary("B32")这种通过文件名加载的方式,也会补全绝对路径这一论断。在源码中就这一句,没有给出绝对路径。也不做其他设置。

    调试参数如下:

    图中参数具体为:-Djava.library.path="F:\java32\jdk1.8.0_181\bin\lib" Main,也就是指定java.library.path。

    同样可以看到最终是以绝对路径的方式加载。

    B32映射地址:

    但是使用sys.loadlib,系统自动补全的后缀为dll,此时在用.exe,会在寻找的时候就出错,不会再有后边加载这一过程了。

    A类问题总结:java先根据给出的绝对路径,或补足的路径,去寻找是否有这个文件;如果有,再由jvm试图去加载这个文件,此时如果该文件为合法的dll(win32)文件,加载过程才算成功。当文件不存在,或者不是合法的dll(win32)文件,都会出现错误。

    B类错误,在所有成功加载的dll文件中,找不到要调用的方法:

    java在调用一个native方法的时候,会去所有已成功加载的dll的导出名称表里寻找与之匹配的方法名,如果照的到,就可以调用。找不到,就会报此类问题。当然,jvm运行的时候不可能每次调用都要这样找一次 ,而是有一套处理办法,看资料是通过一个地址映射表完成。这部分太底层,还没研究懂。我们来看与B类错误关系最为紧密的:只要在dll的导出名称表中找的到java调用的那个方法的名称,就可以成功调用。这里有两个需要注意和验证的地方:

    第一,在导出名称表中找得到,不是单纯的指这个方法存在于导出名称表中,还包括,可以被系统找到。这一点,我在第一篇文章里有解释:dll导出名称表的查找策略。这个问题也是在搞这个java发现的,之前也不知道。

    第二,调用时,仅凭方法名来比对,与方法的参数、返回类型等等都无关。比如说,我在java中要调用的是一个无参无返回值类型的方法void say(),而dll中导出的是一个3个参数,int返回类型的int say(arg1,arg2,arg3),同样可以实现调用。

    第三,成功调用,但不保证方法成功执行。

    以下我们来验证一下:

    java层不变,我们加载B32.dll,试图调用native void say(String s)方法。这个方法经过头文件编译后,得到:

    extern "C" JNIEXPORT void JNICALL Java_Native_say (JNIEnv *, jclass, jstring)。也就是说,在B32.dll里边要找Java_Native_say方法。extern "C"、JNIEXPORT以及JNICALL为C语言基础知识,不了解的可以自己查一下。

    我们先做“常规”的流程。在C中方法:

    extern "C" JNIEXPORT void JNICALL Java_Native_say(JNIEnv *env, jclass cla, jstring s){
    
           puts("VS2017");
    
           printf("%d\n", s);
    
           printf("%X\n", &s);
    
           const char* temp = env->GetStringUTFChars(s, 0);
    
           puts(temp);
    
    }

    编译好Dll后,查看PE结构,得到整个方法的内存地址:

    dbg调试,按上文调试的方法,找到方法的入口,下断点,单步运行。可见,程序的确跑到这里,5个call对应C源码中的puts、printf、printf、env->GetStringUTFChars以及puts,除第四个cal是字符串转换外,其他四个每次执行完,都会得到一条对应的输出。

    这说明,java调用dll的方法,在内存中,是要跑到对应的方法内存地址的。

    基于此,我们来验证“调用时,仅凭方法名来比对,与方法的参数、返回类型等等都无关”。

    java层不变,我们把原来的B32.dll移走。在系统目录windows下,随便找一个dll。

    粘贴到我们的lib目录中,改名B32.dll。

    然后把这个dll导出名称表的一个函数名称改为Java_Native_say。鉴于dll名称表的查找策略,这里最好直接改名称表的第一项,用十六进制编辑器来改。

    原来第一项的名称为“AccConvertAccessMaskToActrlAccess”。然后,再查一下函数地址:

    好了,开始dbg调试。得到这个伪"B32.dll"的映像地址,加上RVA,下断点:

    说明的确是进入到这方法了。然后放开继续运行的话,是有可能出错的。我们这里改的这个就报错了。当然有时候也不会出错,即便方法的修饰符不一致。

    好了,现在可以确定,java调用dll中的方法,和C程序调用dll方法,基本一致的,都是在dll导出表中寻找,并且是以C的方式完成的。

    那正常开发,根据自动生成的.h文件,直接copy方法声明,然后再实现生成dll,为什么会找不到呢?

    其实,严格按照我上边给出的方法,如果编译32位的dll,的确是找不到的。对于extern "C" JNIEXPORT void JNICALL Java_Native_say (JNIEnv *, jclass, jstring)这种方法定义,无论用eclipse,还是VS6,VS2017,导出表的名称将会是_Java_Native_say@12,而不会得到Java_Native_say。我是编译好后,改的16进制数据,如同上边做验证那样,把方法名改了。这种方式明显不行,如果导出方法很多,一个一个改岂不是很费劲。eclipse编辑C使用mingw,如果是32位的,是有参数可以修改,直接生成无附加符号的方法;VS的话,我还没找到方法。如果是64位平台,无论eclipse还是VS,就会直接生成“Java_Native_say”。

    另外,再多说点。在编写native源码的时候,如果不在头文件声明,也不在.cpp/c中声明,而是仅有定义,那么extern "C" JNIEXPORT void JNICALL Java_Native_say (JNIEnv *, jclass, jstring)这行必须保证完整,一个词不能少;如果是先声明,再定义,生命时必须保证完整,定义时,extern "C" JNIEXPORT都可以省略,只写 void JNICALL Java_Native_say (JNIEnv *, jclass, jstring)即可。

    最后,理解上述AB问题的内容后,我们可以推测,也可以验证:

    1.System.load/loadLibrary不一定要写在声明native方法的那个类中,也非必须写在static{}里。在代码的任何地方,只要是调用native方法前调用sys.load即可。只不过放在native类的static{}中,可以保证不会漏掉加载过程。

    2.可以load多个dll;如果这些dll中有同名方法,调用时,将调用第一次load成功的那个dll里的该方法。

    总结一下要点:

    第一:准确的给出路径,保证java能够找到dll文件;合法的dll,让jvm能够成功的加载dll。

    第二:准确的dll导出名称表的方法名称,让jvm可以找到native方法所在的内存地址,完成调用。

    展开全文
  • java调用C++ DLL库方法

    千次阅读 2018-08-14 14:38:24
    摘要: 最近一个项目要开发网页端人脸识别项目,人脸识别的算法已经写好,是C++版,但是网页端要求使用Java后台,这就涉及到Java调用DLL的问题。经过查找,实现了一个简单的例子。 1、第一步,先在Java中新建一个类...

    原文链接

    摘要: 最近一个项目要开发网页端人脸识别项目,人脸识别的算法已经写好,是C++版,但是网页端要求使用Java后台,这就涉及到Java调用DLL的问题。经过查找,实现了一个简单的例子。 1、第一步,先在Java中新建一个类     如上图,其中注意这句System.loadLibrary("javaCallcpp");,这就是加载dll文件的代码了。

    最近一个项目要开发网页端人脸识别项目,人脸识别的算法已经写好,是C++版,但是网页端要求使用Java后台,这就涉及到Java调用DLL的问题。经过查找,实现了一个简单的例子。

    1、第一步,先在Java中新建一个类

       

    如上图,其中注意这句System.loadLibrary("javaCallcpp");,这就是加载dll文件的代码了。然后我们需要dll中实现下面定义的加减乘除方法。

    2、编译文件,文件名为Java2cpp.java,首先编译成class文件,如果用的是eclipse,这个文件已经自动生成了,在工程目录下的bin文件夹下。用命令行编译,打开cmd窗口,cd到.java文件所在目录,执行命令javac Java2cpp.java,即生成Java2cpp.class

    然后执行命令javah Java2cpp生成Java2cpp.h头文件,但是这一步往往会失败,另一种方法可以成功,进入eclipse项目的目录,进入bin文件夹,执行命令javah -classpath . -jni 包名.类名(com.test.jni.Java2cpp),然后生成了com_test_jni_Java2cpp.h

    3、在VS中新建项目win32项目,命名为:TestJNI第二步选择如下:

    4、把第二步生成的头文件拷进项目文件夹,然后导入。

    5、实现头文件中的方法:

    (1)新建头文件dllApi.h,代码如下:

    #include "com_test_jni_Java2cpp.h"
    
    int DLL_API_ADD(int a, int b);
    int DLL_API_SUB(int a, int b);
    int DLL_API_MUL(int a, int b);
    int DLL_API_DIV(int a, int b);

     

    (2)新建dllApi.cpp实现上述方法,代码如下:

    复制代码

    复制代码

    #include "stdafx.h"
    #include <iostream>
    #include "dllApi.h"
    
    int DLL_API_ADD(int a, int b)
    {
      return (a + b);
    }
    
    int DLL_API_SUB(int a, int b)
    {
      return (a - b);
    }
    
    int DLL_API_MUL(int a, int b)
    {
      return (a*b);
    }
    
    int DLL_API_DIV(int a, int b)
    {
      return (a / b);
    }

    复制代码

    复制代码

     

    (3)在TestJNI.cpp中添加代码实现com_test_jni_Java2cpp.h方法,添加完之后代码如下:

    复制代码

    复制代码

    // TestJNI.cpp : 定义 DLL 应用程序的导出函数。
    //
    
    #include "stdafx.h"
    #include "TestJNI.h"
    #include "com_test_jni_Java2cpp.h"
    #include "dllApi.h"
    // 这是导出变量的一个示例
    TESTJNI_API int nTestJNI=0;
    
    // 这是导出函数的一个示例。
    TESTJNI_API int fnTestJNI(void)
    {
      return 42;
    }
    
    // 这是已导出类的构造函数。
    // 有关类定义的信息,请参阅 TestJNI.h
    CTestJNI::CTestJNI()
    {
      return;
    }
    
    JNIEXPORT jint JNICALL Java_com_test_jni_Java2cpp_DLL_1ADD(JNIEnv *env, jobject obj, jint a, jint b){
      int var = 0;
      var = DLL_API_ADD(a, b);
      return var;
    }
    
    JNIEXPORT jint JNICALL Java_com_test_jni_Java2cpp_DLL_1SUB(JNIEnv *env, jobject obj, jint a, jint b){
      int var = 0;
      var = DLL_API_SUB(a, b);
      return var;
    }
    
    JNIEXPORT jint JNICALL Java_com_test_jni_Java2cpp_DLL_1MUL(JNIEnv *env, jobject obj, jint a, jint b){
      int var = 0;
      var = DLL_API_MUL(a, b);
      return var;
    }
    
    JNIEXPORT jint JNICALL Java_com_test_jni_Java2cpp_DLL_1DIV(JNIEnv *env, jobject obj, jint a, jint b){
      int var = 0;
      var = DLL_API_DIV(a, b);
      return var;
    }

    复制代码

    复制代码

     

    (4)生成dll,在项目文件夹下的Debug文件夹内可以找到TestJNI.dll,但是因为我们在Java里要求dll名为JavaCallcpp,所以此时把项目重命名为JavaCallcpp,然后重新生成JavaCallcpp。【这一步生成会失败,按照如下添加路径】

    6 调用方法

    把第五步生成的JavaCallcpp.dll拷进JRE安装路径下的bin文件夹内,运行java程序即可。结果如下:

    展开全文
  • Java如何调用dll

    万次阅读 2018-08-14 13:41:06
    1. 首先有testdll.dll 2. 需要testdll.dll的头文件,以便知道了定义了哪些接口在里面,例如: 当然,要知道这些方法的实现,就要看实现部分了,一般不需要知道。   ---------------------------...
  • Java调用dll文件

    2020-04-21 23:20:55
    近期根据C++做了一个图片质量检测的项目,目前需要在在java中进行调用,所以先在C++上生成dll文件,然后基于java调用dll文件实现功能。 环境: C++:VS2017(之前配置opencv真是要了老命) java:idea2020+jdk1.8。 ...
  • Java通过JNA方式调用DLL

    万次阅读 热门讨论 2018-04-07 09:37:39
    Java调用C++动态链接库的方式很多,有jnative,jna等。这里介绍如何通过jna的方式调用动态链接库。调用代码很简单,就是需要注意几个问题。 1、动态链接库编译时选择的平台。如果通过x86平台编译,那么只能使用32位...
  • 几种java调用dll的方式

    万次阅读 2016-05-27 09:38:00
    Java调用DLL有多种方式,常用的方式有JNative、JNA、JNI等。
  • java调用dll文件的两种方法

    万次阅读 2017-12-14 17:08:23
    https://www.cnblogs.com/huozhong/p/5830485.html JNA地址:http://blog.csdn.net/shendl/article/details/3589676  JNative地址:... 给大家介绍一个最新的访问本机代码的Jav
  • 提供JNA4.3最新版本,简单dll调用demo方法源码。 解压编译后即可运行
  • 1、写出java 文件:public class JavaCall{ static { System.loadLibrary("calldll"); } public native static int shanfei(int i); public static void main(String[] args) { JavaCall jc = new J
  • java调用动态库时,.dll文件路径问题

    万次阅读 2016-05-09 10:26:47
    网上说”.dll”应该放在”.class”同级目录下,但经测试,程序报错: “java.lang.UnsatisfiedLinkError: no JniDllDemo in java.library.path” 当放到java工程同级目录时,一个例子可以调用: 当我调用vtk...
  • java调用dll文件的几种常见方式

    万次阅读 2018-02-09 16:40:03
    java调用动态库需要关注的问题: 1. 如何装载DLL文件,以及如何定位所要使用的方法; 2. 数据类型如何对应; 3. 如何给使用的方法传递参数; 4. 如何获取返回的值。一. 数据类型对应关系 Java Type C Type ...
  • java调用C#编写的dll

    千次阅读 2017-07-31 11:38:09
    由于之前用过在java调用C语言的dll,所以开始是考虑把C#的dll用C封装成新的dll,然后使用JNA在java调用,但是实际操作的时候,由于C调用C#需要使用托管语言,由于本人对于C及C#都不了解,在C封装C#导出成dll的...
  • Java如何调用C#写的".dll"类库

    千次阅读 2018-05-14 17:23:11
    属性系统是在客户端由C#生成的dll类库,服务端要想使用,就需要Java调用该C#生成的dll类库。但是:java 并不能直接调用 C# 的 dll,步骤如下:1. 下载jni4net最新版(我的为jni4net-0.8.8.0-bin.zip)2. 配置环境...
  • 关于Java调用32/64位dll的问题

    万次阅读 2016-04-20 12:39:19
    最近在做一个项目,需要用到java的jni接口调用C++,本来很简单的一件事,却把我搞得晕头转向。是什么问题呢?32位与64位的问题。 由于C++要调用一个已经编译好,且没有源码的32位dll文件,所以最后编译好的dll是32...
  • 但是程序运行需要调用dll文件来完成功能 解决办法 将调用dll文件的方法由相对路径改为绝对路径 //例如调用海康摄像头中的一个地方//官方给的HCNetSDK.java中使用的是相对路径,要求库文件都方法项目根目录下 ...
  • 程序要求: java使用JNA调用HWDll.dll;...在使用JAVA的JNA调用时,HWDll.dll可以调成功访问函数方法,但USERDll.dll调用(loadLibrary)失败,错误为:126。 麻烦大家帮忙看一下,最好给一个demo的例子,谢谢。
  • 而在dll中,调用的函数和你的路径是有关系的,因此,报错。 解决方法有二: 一、在他那重新生成 .class .h之类的,也就是要重新做一个dll,但是呢,很麻烦。 二、确定他的路径,自己去根据JNI的命名规则去改dl...
  • java调用dll时回调函数的实现(jna)

    千次阅读 2017-08-21 22:45:36
    java调用dll文件需要使用回调函数作为公开函数的参数时,用以下方法实现: 首先,看c++中定义的dll公开函数: typedef void (*ccback)(char *name ,int length); int dllFunction(char *ip, int port, ccback cc)...
1 2 3 4 5 ... 20
收藏数 56,293
精华内容 22,517
关键字:

java调用dll