Java - 通过相对路径加载dll并将它们隐藏在jar中

时间:2011-01-14 12:35:26

标签: java jar java-native-interface loading relative-path

第1部分

我正在开发一个应该作为jar发布的Java应用程序。该程序依赖于JNI调用的C ++外部库。要加载它们,我使用方法System.load和绝对路径,这很好。

但是,我真的想在JAR中“隐藏”它们,所以我创建了一个包来收集它们。这迫使我加载一个相对路径 - 包路径。通过这种方法,我让用户在任何目录中运行JAR,而不用担心链接DLL或无聊以前的安装过程。

这会引发预期的异常:

  

线程“main”中的异常java.lang.UnsatisfiedLinkError:期望库的绝对路径

我怎样才能使这个工作?

第2部分

将DLL复制到文件夹(如下所述)的方法仅在我在eclipse环境下运行时才有效。运行导出的JAR,DLL二进制文件很好地创建,但加载JNI会引发下一个异常:

  

线程“main”中的异常java.lang.reflect.InvocationTargetException

 at org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader.main(JarRsrcLoader.java:56)
 Caused by: java.lang.UnsatisfiedLinkError: C:\Users\Supertreta\Desktop\nm files\temp\jniBin.dll: Can't find dependent libraries at java.lang.ClassLoader$NativeLibrary.load(Native Method)

我运行这个加载方法:

public static void loadBinaries(){
        String os = System.getProperty("os.name").toLowerCase();

        if(os.indexOf("win") >= 0){
            ArrayList<String> bins = new ArrayList<String>(){{
                add("/nm/metadata/bin/dependence1.dll");
                add("/nm/metadata/bin/dependence2.dll");
                add("/nm/metadata/bin/dependence3.dll");
                add("/nm/metadata/bin/dependence4.dll");
                add("/nm/metadata/bin/jniBin.dll");
            }};

            File f = null;
            for(String bin : bins){
                InputStream in = FileManager.class.getResourceAsStream(bin);
                byte[] buffer = new byte[1024];
                int read = -1;
                try {
                    String[] temp = bin.split("/");
                    f = new File(TEMP_FOLDER, temp[temp.length-1]);     
                    FileOutputStream fos = new FileOutputStream(f);

                    while((read = in.read(buffer)) != -1) {
                        fos.write(buffer, 0, read);
                    }
                    fos.close();
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            System.load(f.getAbsolutePath());
        }
    }

我认为这可能是一个访问权限问题,但不知道如何解决它。你觉得怎么样?

4 个答案:

答案 0 :(得分:26)

我不相信你可以直接从JAR加载DLL。您必须采取中间步骤将DLL复制出JAR。以下代码应该这样做:

public static void loadJarDll(String name) throws IOException {
    InputStream in = MyClass.class.getResourceAsStream(name);
    byte[] buffer = new byte[1024];
    int read = -1;
    File temp = File.createTempFile(name, "");
    FileOutputStream fos = new FileOutputStream(temp);

    while((read = in.read(buffer)) != -1) {
        fos.write(buffer, 0, read);
    }
    fos.close();
    in.close();

    System.load(temp.getAbsolutePath());
}

答案 1 :(得分:2)

基本上这应该有效。由于这是JNA的方式,只需下载并研究代码即可。你甚至有一些提示让这个平台独立......

编辑

JNA将其本机代码放在jar中,在运行时解压缩正确的二进制文件并加载它。这可能是一个很好的模式(如果我的问题是正确的)。

答案 2 :(得分:2)

这个JarClassLoader旨在解决同样的问题:

http://www.jdotsoft.com/JarClassLoader.php

答案 3 :(得分:-1)

您需要使用DLL的位置填充类加载器 - 但是可以在不从jar中提取它的情况下加载它。在执行加载调用之前的一些简单就足够了。在您的主要课程中添加:

static {
    System.loadLibrary("resource/path/to/foo"); // no .dll or .so extension!
}

值得注意的是,我的体验基本相同issue with JNA and OSGi's handling of how the DLLs are loaded