我使用JNA用Java包装共享库(用C语言编写)。共享库是内部编写的,但该库使用来自另一个外部库的函数,这又取决于另一个外部库。所以情况是这样的:
ext1 < - ext2 < - 内部
即内部使用外部库ext2,它再次使用外部库ext1。我试过的是:
System.loadLibrary("ext1");
System.loadLibrary("ext2");
NativeLIbrary.loadLibrary("internal",xxx.class);
加载库“ext2”时,此方法失败并显示“UnresolvedException”;链接器抱怨存在于库“ext1”中的符号。所以它认为System.loadLibrary()函数不会使“ext1”中的符号全局可用?当使用stdlib函数dlopen()时:
handle = dlopen( lib_name , RTLD_GLOBAL );
在@lib_name中找到的所有符号都可用于后续加载中的符号解析;我想我喜欢的东西类似于java品种System.loadLibrary()?
问候 - Joakim Hove
这是一个老问题,但我找到了一个可接受的解决方案,它也应该是可移植的,我想我应该发布一个答案。解决方案是使用JNA的NativeLibrary#getInstance()
,因为在Linux上,这会将RTLD_GLOBAL
传递给dlopen()
(在Windows上这不是必需的)。
现在,如果您使用此库来实现Java native
方法,则在调用System.load()
之后,还需要在同一个库上调用Sysem.loadLibrary()
(或NativeLibrary#getInstance()
)。
首先,链接到JNA错误:JNA-61
那里的评论说,基本上应该在实际库之前加载依赖关系,以便在JNA中使用,而不是标准的Java方式。我只是复制粘贴我的代码,这是一个典型的场景:
String libPath =
"/path/to/my/lib:" + // My library file
"/usr/local/lib:" + // Libraries lept and tesseract
System.getProperty("java.library.path");
System.setProperty("jna.library.path", libPath);
NativeLibrary.getInstance("lept");
NativeLibrary.getInstance("tesseract");
OcrTesseractInterf ocrInstance = (OcrTesseractInterf)
Native.loadLibrary(OcrTesseractInterf.JNA_LIBRARY_NAME, OcrTesseractInterf.class);
我编写了一个小型库,使用Tesseract为我的Java应用程序提供OCR功能。 Tesseract依赖于Leptonica,因此要使用我的库,我需要首先加载库lept和tesseract。使用标准方法(System.load()和System.loadLibrary())加载库不起作用,设置属性jna.library.path或java.library.path也不起作用。显然,JNA喜欢以自己的方式加载库。
这在Linux中适用于我,我想如果设置了正确的库路径,这也适用于其他操作系统。
好;
我最终找到了一个可接受的解决方案,但并非没有大量的箍。我做的是
它实际上似乎工作:-)
还有另一种解决方案。您可以直接在JNI代码中进行dlopen,如下所示:
void loadLibrary() {
if(handle == NULL) {
handle = dlopen("libname.so", RTLD_LAZY | RTLD_GLOBAL);
if (!handle) {
fprintf(stderr, "%s\n", dlerror());
exit(EXIT_FAILURE);
}
}
}
...
...
loadLibrary();
这样,您将使用RTLD_GLOBAL打开库。
你可以在这里找到详细的描述:http://www.owsiak.org/?p=3640
试试这个,将此函数添加到您的代码中。在加载dll之前调用它。对于参数,请使用dll的位置。
public boolean addDllLocationToPath(String dllLocation)
{
try
{
System.setProperty("java.library.path", System.getProperty("java.library.path") + ";" + dllLocation);
Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
fieldSysPath.setAccessible(true);
fieldSysPath.set(null, null);
}
catch (Exception e)
{
System.err.println("Could not modify path");
return false;
}
return true;
}
}
正如http://www.owsiak.org/?p=3640所述,Linux上一个简单而粗糙的解决方案是使用LD_PRELOAD
。
如果这是不可接受的,那么我建议Oo.oO的答案:dlopen
在JNI代码中使用RTLD_GLOBAL
的库。