博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Jni开发时,dll文件放置的路径处理方式
阅读量:5221 次
发布时间:2019-06-14

本文共 3611 字,大约阅读时间需要 12 分钟。

JACOB是一个较成熟的开源项目,可以很方便的调用COM组件。搞过JNI的都知道,本地库要放到系统path中,这样,Java进程在运行中才能找到本地库并动态加载。我们可以通过环境变量System.getProperty("java.library.path")来查看当前JVM搜索本地库的路径。 


这时,就会遇到一个问题,部署应用的时候要记住将本地库拷贝到环境变量path指定的路径中。一般在windows平台上直接copy到C:\WINDOWS\System32目录下了事。但要换一台机器部署怎么办?除了要把Java程序拿过去,还要记的把本地库也copy到正确的目录,真麻烦。于是想看看有什么好办法来解决这个问题。 


首先,最容易想到的是,把本地库和class文件放在一起,利用Class.getResource(str)找到路径,然后加到环境java.library.path中: 


代码 

URL url = Foo.class.getResource("Foo.class");   

String path = (new File(url.getPath())).getParent();   

System.setProperty("java.library.path", path);  


看上去很好,但却不能工作。查了一下ClassLoader的源代码,原来它把搜索路径定义为静态变量并只初始化一次,后面再设置java.library.path就没有用了。ClassLoader代码片断: 

代码 

// The paths searched for libraries   

static private String usr_paths[];   

static private String sys_paths[];   

...   

if (sys_paths == null) {   

    usr_paths = initializePath("java.library.path");   

    sys_paths = initializePath("sun.boot.library.path");   

}  


正在一筹莫展是,翻看JACOB的源代码,忽然有了惊喜的发现。 

代码 

try  

{   

    //Finds a stream to the dll. Change path/class if necessary   

    InputStream inputStream = getClass().getResource("/jacob.dll").openStream();   

    //Change name if necessary   

    File temporaryDll = File.createTempFile("jacob", ".dll");   

    FileOutputStream outputStream = new FileOutputStream(temporaryDll);   

    byte[] array = new byte[8192];   

    for (int i = inputStream.read(array); i != -1; i = inputStream.read(array)) {   

                outputStream.write(array, 0, i);   

        }   

    outputStream.close();   

    temporaryDll.deleteOnExit();   

    System.load(temporaryDll.getPath());   

    return true;   

}   

catch(Throwable e)   

{   

    e.printStackTrace();   

    return false;   

}  


高,真是好办法。和我一样,把dll放在classpath中,用Class.getResource(str).openStream()读取这个dll,然后写到temp目录中,最后用System.load(path)来动态加载。多说一句,为什么得到了jacob.dll的URL不直接去加载呢?想想看,如果把dll和class一起打成Jar包,ClassLoader还是不能加载本地库,因为System.load(path)需要的是dll的完整路径,但并不支持jar协议。还不明白,看看下面的代码: 

代码 

URL url = Foo.class.getResource("/java/lang/String.class");   

System.out.println(url.toExternalForm());   

System.out.println(url.getFile());  


在我的机器上的运行结果是: 

代码 

jar:file:/D:/jdk1.5.0_06/jre/lib/rt.jar!/java/lang/String.class  

file:/D:/jdk1.5.0_06/jre/lib/rt.jar!/java/lang/String.class  


ClassLoader中用new File(name),当然会找不到文件。同时,看看我的第一种方法,就算能设置成功环境java.library.path,如果dll是在jar包中,还是加载不了。 



到现在,你是不是觉得问题已经解决了?还没呢!jacob的很多源文件中已经写了下面的代码: 


代码 

static {   

    System.loadLibrary("jacob");   

}  


除非我去掉这一句重新编译jacob的源代码,否则系统还是会报错。不过,既然有了上面的想法,稍微变通一下,就可以巧妙的解决。首先找到环境java.library.path,然后把dll拷贝到其中一个路径中就行了。 

代码 

static {   

    try {   

        String libpath = System.getProperty("java.library.path");   

        if ( libpath==null || libpath.length() == 0 ) {   

            throw new RuntimeException("java.library.path is null");   

        }   

               

        String path = null;   

        StringTokenizer st = new StringTokenizer(libpath, System.getProperty("path.separator"));   

        if ( st.hasMoreElements() ) {   

            path = st.nextToken();   

        } else {   

            throw new RuntimeException("can not split library path:" + libpath);   

        }   

               

        InputStream inputStream = Foo.class.getResource("jacob.dll").openStream();   

        final File dllFile = new File(new File(path), "jacob.dll");   

        if (!dllFile.exists()) {   

            FileOutputStream outputStream = new FileOutputStream(dllFile);   

            byte[] array = new byte[8192];   

            for (int i = inputStream.read(array); i != -1; i = inputStream.read(array)) {   

                outputStream.write(array, 0, i);   

            }   

            outputStream.close();   

        }   

        //dllFile.deleteOnExit();      

        Runtime.getRuntime().addShutdownHook(new Thread(){   

            public void run() {   

                if ( dllFile.exists() ) {   

                    boolean delete = dllFile.delete();   

                    System.out.println("delete : " + delete);   

                }   

            }   

        });   

        } catch (Throwable e) {   

            throw new RuntimeException("load jacob.dll error!", e);   

    }   

}  


唯一的美中不足,在系统关闭的时候删除dll总是不能成功,试了两种办法都不行。想想也对,dll正被程序使用,当然不能删除。翻了一下API,Java好像没用提供unload本地库的功能,只好做罢。 

转载于:https://www.cnblogs.com/java0721/archive/2012/05/31/2602985.html

你可能感兴趣的文章
CSS编写技巧
查看>>
linux中fork()函数详解 ...
查看>>
12个常用的js正则表达式
查看>>
win7:你需要来自Administrators的权限才能对此文件进行修改的一个文件
查看>>
正则表达式划分CSV
查看>>
笔记 2、分布式系统基础设施
查看>>
git
查看>>
Cisco设备管理
查看>>
【ACM从零开始】LeetCode OJ-Delete Node in a Linked List
查看>>
矢量图形比对模块
查看>>
Java集合框架学习
查看>>
第16周总结
查看>>
将Cent0S 7的网卡名称eno33改为eth0
查看>>
透明度Opacity多浏览器兼容处理
查看>>
oracle 常用简单命令语句
查看>>
【机器学习_3】常见术语区别
查看>>
Oracle基础 数据库备份和恢复
查看>>
C#编程时应注意的性能处理
查看>>
Java集合--概述
查看>>
1-TwoSum(简单)
查看>>