2011-01-14 67 views
16

PART 1

我正在开发一个Java应用程序,该应用程序应该作为jar发布。该程序依赖于由JNI调用的C++外部库。要加载它们,我使用方法System.load与绝对路径,这工作正常。但是,我真的想将它们“隐藏”在JAR内部,所以我创建了一个包来收集它们。这迫使我加载一个相对路径 - 包路径。通过这种方法,我让用户在任何目录下运行JAR,而不用担心链接DLL或无聊与以前的安装过程。Java - 通过相对路径加载dll并将它们隐藏在瓶子里

这将引发预期的异常:

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

我怎样才能得到这个工作?

PART 2

复制DLL文件到文件夹的方式(下文解释),只有当我在Eclipse环境下运行它的工作原理。运行导出的JAR,该DLL二进制文件以及创建,但加载JNI一个抛出一个异常:

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

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

+0

你为什么要隐藏呢? – 2011-01-14 12:37:04

+0

提供一个干净的罐子 – supertreta 2011-01-14 12:39:17

回答

21

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

这似乎是解决这个问题的一种快捷方式。我尝试过,但有一个问题:createTempFile方法在文件的名称后附加一个数字,即即使使用这些参数(name,“”),lib“hello.dll”也变为“hello.dll4975093656535427331”。我的主要JNI DLL也依赖于其他DLL,所以我必须知道名称。你知道我该如何处理这个问题? – supertreta 2011-01-14 15:20:23

2

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

编辑

JNA带来了原生代码一起在罐子里,解压在运行时加载UND它正确的二进制文件。这可能是一个很好的模式(如果我的问题是正确的)。

+3

对不起,你错过了你的答案?用“这个”你指的是什么?谢谢 – supertreta 2011-01-14 13:14:04

-1

您需要总理与DLL的位置的类加载器 - 但它可以在不从罐子里取出它被装载。在执行加载调用之前的简单事情就足够了。在你的主类添加:

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