有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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

第一部分

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

然而,我真的想把它们“隐藏”在罐子里,所以我创建了一个包来收集它们。这迫使我加载一个相对路径——包路径。通过这种方法,我允许用户在任何目录中运行JAR,而不必担心链接DLL或厌倦以前的安装过程

这将引发预期的异常:

Exception in thread "main" java.lang.UnsatisfiedLinkError: Expecting an absolute path of the library

我怎样才能让它工作

第二部分

只有在eclipse环境下运行DLL时,才可以将DLL复制到文件夹(如下所述)。运行导出的JAR时,DLL二进制文件创建良好,但加载JNI one会引发下一个异常:

Exception in thread "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) 个答案

  1. # 1 楼答案

    您需要使用DLL的位置来初始化类加载器——但是可以在不从jar中提取的情况下加载它。在执行load调用之前做一些简单的事情就足够了。在主类中添加:

    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

  2. # 2 楼答案

    基本上这应该行得通。由于JNA就是这样做的,所以只需下载它并研究代码即可。你甚至有一些提示让这个平台独立

    编辑

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

  3. # 4 楼答案

    我不相信您可以直接从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());
    }