Java – 通过相对path加载dll并将其隐藏在jar中
第1部分
我正在开发一个应该作为jar发布的Java应用程序。 这个程序依赖于JNI调用的C ++外部库。 要加载它们,我使用绝对path的方法System.load
,这工作正常。
但是,我真的想把它们藏在JAR里面,所以我创build了一个包来收集它们。 这迫使我加载一个相对path – 包path。 通过这种方法,我让用户在任何目录下运行JAR,而不必担心链接DLL或者厌烦以前的安装过程。
这引发了预期的exception:
线程“main”中的exceptionjava.lang.UnsatisfiedLinkError:期望库的绝对path
我怎样才能得到这个工作?
第2部分
将DLL复制到一个文件夹(下面解释)的方法只在我在eclipse环境下运行时才起作用。 运行一个导出的JAR,DLL二进制文件被很好的创build,但是加载JNI会抛出下一个exception:
线程“main”中的exceptionjava.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()); } }
我认为这可能是一个访问权限问题,但不知道如何解决它。 你怎么看?
我不相信你可以直接从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()); }
基本上这应该工作。 正如JNA所做的那样,只需下载并研究代码即可。 你甚至有一些提示,使这个平台独立…
编辑
JNA将其本地代码一起带入jar中,在运行时解压缩正确的二进制文件并加载它。 这可能是一个很好的模式(如果我的问题是正确的)。
这个JarClassLoader旨在解决同样的问题:
您需要使用DLL的位置来填充类装入器 – 但是可以在不从jar中提取它的情况下加载它。 加载调用执行之前的简单事情就足够了。 在你的主类中添加:
static { System.loadLibrary("resource/path/to/foo"); // no .dll or .so extension! }
值得注意的是,我经历了与JNA和OSGi处理如何加载DLL基本相同的问题 。