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

发布于 2024-10-12 04:36:50 字数 2241 浏览 11 评论 0原文

第 1 部分

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

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

这会引发预期的异常:

线程“main”java.lang.UnsatisfiedLinkError中出现异常:需要库的绝对路径

如何才能使其正常工作?

第 2 部分

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

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

 位于 org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader.main(JarRsrcLoader.java:56)
 引起原因:java.lang.UnsatisfiedLinkError:C:\ Users \ Supertreta \ Desktop \ nm files \ temp \ jniBin.dll:在java.lang.ClassLoader $ NativeLibrary.load(本机方法)中找不到依赖库

我运行此加载方法:

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());
        }
    }

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

PART 1

I am developing a Java application that should be release as a jar. This program depends on C++ external libraries called by JNI. To load them, I use the method System.load with an absolute path and this works fine.

However, I really want to "hide" them inside the JAR, so I have created a package to collect them. This forces me to load an relative path - the package path. By this approach, I let the user run the JAR in any directory, without being worried about linking the DLLs or bored with a previous installation process.

This throws the expected exception:

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

How can I get this working?

PART 2

The approach of copying the DLLs to a folder (explained below) only works when I run it under the eclipse environment. Running an exported JAR, the DLL binaries are well created but loading the JNI one throws the next exception:

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)

I run this loading 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());
        }
    }

I think this could be an access privileges issue, but don't know how to solve it. What do you think?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

十二 2024-10-19 04:36:50

我不相信你可以直接从 JAR 加载 DLL。您必须执行从 JAR 中复制 DLL 的中间步骤。下面的代码应该可以做到这一点:

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());
}

I don't believe you can load the DLL directly from the JAR. You have to take the intermediary step of copying the DLL out of the JAR. The following code should do it:

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());
}
白馒头 2024-10-19 04:36:50

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

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

This JarClassLoader aiming to solve the same problem:

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

昨迟人 2024-10-19 04:36:50

基本上这应该有效。由于 JNA 就是这样做的,因此只需下载它并研究代码即可。您甚至有一些提示可以使该平台独立...

编辑

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

Basically this should work. As this is the way JNA does it, simply download it and study the code. YOu even have some hints to make this platform independent...

EDIT

JNA brings its native code along in the jar, unpacks the correct binary at runtime und loads it. This may be a good pattern to follow (if i got your question correct).

江心雾 2024-10-19 04:36:50

您需要使用 DLL 的位置来启动类加载器——但无需从 jar 中提取它就可以加载它。在执行加载调用之前做一些简单的事情就足够了。在你的主类中添加:

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

值得注意的是,我遇到了与 JNA 基本相同的 问题以及 OSGi 对 DLL 加载方式的处理

You need to prime the classloader with the location of the DLL -- but it can be loaded without extracting it from the jar. Something simple before the load call is executed is sufficient. In your main class add:

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

Notably, I experienced basically the same issue with JNA and OSGi's handling of how the DLLs are loaded.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文