简体   繁体   English

Java 在 jar 中加载本机库时出现问题

[英]Java problem loading native lib inside jar

(I slightly re-edited my post as I found out that I need just one native lib, not all 4 as previously thought) (我稍微重新编辑了我的帖子,因为我发现我只需要一个本地库,而不是之前认为的全部 4 个)

I am on Windows 7 x64, and I try to pack my native lib into the JAR using this code (BTW I also tried basically all the other codes from that page - result the same for all of them in my case.!!).我在 Windows 7 x64 上,我尝试使用代码将我的本机库打包到 JAR 中(顺便说一句,我也基本上尝试了该页面中的所有其他代码 - 在我的情况下,所有代码的结果都相同。!!)。

My modded code looks like this:我修改后的代码如下所示:

static {
    try {
        //EXTRACT NATIVE LIB(S) FROM JAR
        String libName = "lwjgl64.dll";
        URL url = Class.class.getResource("/natives/" + libName);
        File tmpDir = new File(App.FILE_APPPATH.getAbsolutePath() + "/libs");
        if (tmpDir.exists()) {
            FileUtils.deleteDirectory(tmpDir);
        }
        tmpDir.mkdir();
        File nativeLibTmpFile = new File(tmpDir, libName);
        try (InputStream in = url.openStream()) {
            Files.copy(in, nativeLibTmpFile.toPath());
        }

        //ADD PATH TO EXTRACTED LIBS FOLDER TO JAVA
        System.setProperty("java.library.path", tmpDir.getAbsolutePath());
        Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
        fieldSysPath.setAccessible(true);
        fieldSysPath.set(null, null);
        //System.err.println("'" + System.getProperty("java.library.path") + "'");

        //LOAD LIB DLL FILE(S)
        //System.err.println("'" + nativeLibTmpFile.getAbsolutePath() + "'");
        System.load(nativeLibTmpFile.getAbsolutePath());
    } catch (IOException | SecurityException | IllegalArgumentException | NoSuchFieldException | IllegalAccessException  ex) {
        Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
    }
}

I added this code to my main class, and run my Java app from the NetBeansIDE - running my java App like this all seem to be OK and working correctly, no error whatsoever我将此代码添加到我的主 class,并从 NetBeansIDE 运行我的 Java 应用程序 - 像这样运行我的 java 应用程序似乎一切正常并且工作正常,没有任何错误

But when I compile my project/App to JAR, run it (that is now already outside the NetBeansIDE started by BAT file) then as soon as there is a method or function that requires that native lib I got this error:但是当我将我的项目/应用程序编译为 JAR 时,运行它(现在已经在由 BAT 文件启动的 NetBeansIDE 之外)然后只要有一个方法或 function 需要本机库我就会收到此错误:

Exception in thread "Thread-12" java.lang.NoClassDefFoundError: org/lwjgl/system/FunctionProvider
        at org.lwjgl.opengl.GL11.<clinit>(GL11.java:38)
        at org.lwjgl.opengl.GLContext.getSupportedExtensions(GLContext.java:228)
        at org.lwjgl.opengl.ContextCapabilities.initAllStubs(ContextCapabilities.java:5802)
        at org.lwjgl.opengl.ContextCapabilities.<init>(ContextCapabilities.java:6240)
        at org.lwjgl.opengl.GLContext.useContext(GLContext.java:374)
        at org.lwjgl.opengl.ContextGL.makeCurrent(ContextGL.java:195)
        at org.lwjgl.opengl.DrawableGL.makeCurrent(DrawableGL.java:110)
        at org.lwjgl.opengl.Display.makeCurrent(Display.java:706)
        at org.lwjgl.opengl.Display.makeCurrentAndSetSwapInterval(Display.java:1025)
        at org.lwjgl.opengl.Display.create(Display.java:852)
        at org.lwjgl.opengl.Display.create(Display.java:797)
        ...

From this I would say it still does not know where the native lib is - right?由此我会说它仍然不知道本地库在哪里 - 对吗? I double checked that the native lib is successfully extracted from the JAR to specified created directory when my app is started via BAT file...and yes, it is.当我的应用程序通过 BAT 文件启动时,我仔细检查了本机库是否已从 JAR 成功提取到指定的创建目录……是的,是的。

My BAT file code looks like this:我的 BAT 文件代码如下所示:

@ECHO OFF
java -XX:+UseG1GC -Xmx1G -server -jar data.jar %*
@if %errorlevel% neq 0 pause

How come it works perfectly as expected when run from NetBeansIDE but it is unable to locate/load already successfully extracted and correctly added native lib file (it is already added directly from inside the app via the code above)?从 NetBeansIDE 运行时,它为何能按预期完美运行,但无法定位/加载已成功提取并正确添加的本机 lib 文件(它已通过上面的代码直接从应用程序内部添加)?

Can anyone tell me what is wrong with my code or how to make it correctly?谁能告诉我我的代码有什么问题或如何正确制作它?

Maybe some values in the BAT file that should not be there when one is using native libs (remember that I do not need to add -Djava.library.path to my BAT file as I have packed my native lib inside the JAR)?也许 BAT 文件中的某些值在使用本机库时不应该存在(请记住,我不需要将-Djava.library.path添加到我的 BAT 文件,因为我已将本机库打包到 JAR 中)? You see: I never used native libs before, this is my first time ever.你看:我以前从未使用过原生库,这是我第一次使用。

BTW I also tried to add that -Djava.library.path to the BAT with the absolute path to the folder with the native libs (I have no spaces in the path), yet the result is absolutely the same...strange, like something in the OS preventing the access tho i have to immediately say probably not, cos it has no problem accessing it from the NetBeansIDE - this is really specific just to the compiled final JAR file + BAT (or at least that is how I see it at the moment).顺便说一句,我还尝试将-Djava.library.path添加到 BAT,其中包含带有本机库的文件夹的绝对路径(我在路径中没有空格),但结果完全相同......奇怪,就像操作系统中的某些东西阻止了访问,但我不得不立即说可能不会,因为它从 NetBeansIDE 访问它没有问题 - 这真的只是针对编译的最终 JAR 文件 + BAT(或者至少我是这样看的)在这一刻)。

UPDATE 2022-05-06更新 2022-05-06

After several other tests I am very close to believe this issue might actually be caused by the Windows 7 x64 OS dealing with file permissions, according to DLL file permissions causes not working post.经过几次其他测试后,我非常接近相信这个问题实际上可能是由处理文件权限的 Windows 7 x64 操作系统引起的,根据DLL 文件权限导致无法正常工作 But if that is the case, how come others have no such problems when deploying their java apps with native libs...or is it really some sort of Windows 7 x64 specific issue?但如果是这样的话,为什么其他人在使用本机库部署他们的 java 应用程序时没有这样的问题......或者它真的是某种 Windows 7 x64 特定问题吗? Also I do not know how to make it work cos I already added full permission for the whole directory of my java projects yet it still behaves the same.我也不知道如何让它工作,因为我已经为我的 java 项目的整个目录添加了完全权限,但它的行为仍然相同。

First off some good advices:首先给出一些好的建议:

  • remove all unnecessary jar files from your project (they might cause problems similar to yours)从您的项目中删除所有不必要的 jar 文件(它们可能会导致与您类似的问题)
  • do not mix different versions of LWJGL jar files (might cause problems similar to yours)不要混合不同版本的 LWJGL jar 文件(可能会导致与您类似的问题)
  • do not mix different versions of LWJGL native.dll files (might cause problems similar to yours)不要混合不同版本的 LWJGL native.dll 文件(可能会导致与您类似的问题)
  • use only really needed native.dll files (sometime unnecessary ones may cause errors like yours)只使用真正需要的 native.dll 文件(有时不必要的文件可能会导致像你这样的错误)

Now here is your code updated by me for automatic detection of all native files in internal natives folder without need of manually defining them + rest is basically yours - tested myself, all works as it should.现在这是我更新的代码,用于自动检测内部 natives 文件夹中的所有本机文件,而无需手动定义它们 + rest 基本上是你的 - 我自己测试过,一切正常。

//INITIALIZATION OF NATIVE LIBS PACKED INSIDE THE COMPILLED JAR
static {
    try {
        String internalNativesFolder = "natives";

        //EXTRACT NATIVE LIB(S) FROM JAR
        File tmpDir = new File(App.FILE_APPPATH.getAbsolutePath() + App.LOMKA_R + internalNativesFolder);
        if (tmpDir.exists()) {
            FileUtils.deleteDirectory(tmpDir);
        }
        tmpDir.mkdir();

        //GETS ALL FILES IN INTERNAL NATIVES FOLDER
        URI uri = App.class.getResource(App.LOMKA_R + internalNativesFolder).toURI();
        Path myPath;
        if (uri.getScheme().equals("jar")) {
            FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.<String, Object>emptyMap());
            myPath = fileSystem.getPath(App.LOMKA_R + internalNativesFolder);
        } else {
            myPath = Paths.get(uri);
        }
        Stream<Path> walk = Files.walk(myPath, 1);
        List<String> libNames = new ArrayList<>();
        for (Iterator<Path> it = walk.iterator(); it.hasNext();) {
            String fN = it.next().getFileName().toString();
            if (!fN.equals(internalNativesFolder)) {
                libNames.add(fN);
            }
        }

        //COPIES THOSE FILES TO TEMPORARY LOCAL DIRECTORY
        for (String libName : libNames) {
            URL url = Class.class.getResource(App.LOMKA_R + internalNativesFolder + App.LOMKA_R + libName);
            File nativeLibTmpFile = new File(tmpDir, libName);
            try (InputStream in = url.openStream()) {
                Files.copy(in, nativeLibTmpFile.toPath());
            }
        }

        //ADD PATH TO EXTRACTED LIBS FOLDER TO JAVA
        System.setProperty("java.library.path", tmpDir.getAbsolutePath());
        Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
        fieldSysPath.setAccessible(true);
        fieldSysPath.set(null, null);

        //LOAD LIB DLL FILE(S)
        libNames.forEach(libName -> {
            System.load(new File(tmpDir, libName).getAbsolutePath());
        });

    } catch (IOException | SecurityException | IllegalArgumentException | NoSuchFieldException | IllegalAccessException | URISyntaxException ex) {
        Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM