我使用JNA使用Java包装共享库(用C语言编写)。共享库是内部编写的,但该库使用来自另一个外部库的函数,这又取决于另一个外部库。所以情况是这样的:
ext1< - ext2< - internal
即。内部使用外部库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
答案 0 :(得分:4)
这是一个老问题,但我找到了一个可接受的解决方案,也应该是可移植的,我想我应该发布一个答案。解决方案是使用JNA的NativeLibrary#getInstance()
,因为在Linux上,这会将RTLD_GLOBAL
传递给dlopen()
(在Windows上不需要这样做)。
现在,如果您使用此库来实现Java native
方法,则在调用{{{{}}后,您还需要在同一个库上调用System.load()
(或Sysem.loadLibrary()
) 1}}。
首先,指向JNA错误的链接:JNA-61
那里的评论说,基本上应该在实际库之前加载依赖项以在JNA中使用,而不是标准的Java方式。我只是复制粘贴我的代码,这是典型的情形:
NativeLibrary#getInstance()
我编写了一个小型库,使用Tesseract为我的Java应用程序提供OCR功能。 Tesseract依赖于Leptonica,因此要使用我的库,我需要首先加载库 lept 和 tesseract 。使用标准方法(System.load()和System.loadLibrary())加载库不起作用,设置属性 jna.library.path 或 java.library也不起作用。 。路径。显然,JNA喜欢以自己的方式加载库。
这对我在Linux中有用,我想如果设置了正确的库路径,这也适用于其他操作系统。
答案 1 :(得分:2)
行;
我最终找到了一个可接受的解决方案,但并非没有大量的箍。我的工作是
它实际上似乎有效: - )
答案 2 :(得分:2)
还有另一种解决方案。您可以直接在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
答案 3 :(得分:0)
试试这个,将此功能添加到您的代码中。在加载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;
}
}
答案 4 :(得分:0)
如http://www.owsiak.org/?p=3640所述,在Linux上一个简单但粗略的解决方案是使用LD_PRELOAD
。
如果这是不可接受的,那么我建议Oo.oO回答:在JNI代码中将dlopen
与RTLD_GLOBAL
一起使用。
答案 5 :(得分:0)
为了解决您的问题,您可以使用以下软件包:https://github.com/victor-paltz/global-load-library。它直接使用RTLD_GLOBAL标志加载库。
这里是一个例子:
import com.globalload.LibraryLoaderJNI;
public class HelloWorldJNI {
static {
// Loaded with RTLD_GLOBAL flag
try {
LibraryLoaderJNI.loadLibrary("/path/to/my_native_lib_A");
} catch (UnsatisfiedLinkError e) {
System.Println("Couldn't load my_native_lib_A");
System.Println(e.getMessage());
e.printStackTrace();
}
// Not loaded with RTLD_GLOBAL flag
try {
System.load("/path/to/my_native_lib_B");
} catch (UnsatisfiedLinkError e) {
System.Println("Couldn't load my_native_lib_B");
System.Println(e.getMessage());
e.printStackTrace();
}
}
public static void main(String[] args) {
new HelloWorldJNI().sayHello();
}
private native void sayHello();
}
它使用与以前的答案相同的dlopen()技巧,但打包在独立代码中。