【问题标题】:java.lang.UnsatisfiedLinkError returned when calling native method调用本机方法时返回 java.lang.UnsatisfiedLinkError
【发布时间】:2012-07-10 19:05:33
【问题描述】:

我正在尝试为第三方 DLL 制作 Java 包装器。我创建了自己的 DLL,充当 JNI 和第三方 DLL 之间的中间人。在 java 中,我很好地加载了这个 DLL,但返回了错误 java.lang.UnsatisfiedLinkError: sixense.Sixense.init()Z。什么可能导致此错误,我该如何正确调试?

Main.java:

package sixense_test;

import sixense.ControllerData;
import sixense.Sixense;

public class Main {

    public static ControllerData[] controllers = new ControllerData[4];
    public static boolean quit;

    public static void main(String[] args) {
        if(Sixense.init()) {
            Sixense.setActiveBase(0);
            Sixense.getAllNewestData(controllers);
            System.out.println("X: " + controllers[0].pos[0] + "Y: " + controllers[0].pos[1] + "Z: " + controllers[0].pos[2]);
        }
    }
}

Sixense.java:

package sixense;

public class Sixense {
    public static native boolean init();
    public static native boolean exit();
    public static native int getMaxBases();
    public static native boolean setActiveBase( int base_num );
    public static native boolean isBaseConnected( int base_num );
    public static native int getMaxControllers();
    public static native int getNumActiveControllers();
    public static native boolean isControllerEnabled( int controller_id );
    public static native boolean getAllNewestData( ControllerData[] all_data );
    public static native boolean getAllData( int index_back, ControllerData[] all_data );
    public static native boolean getNewestData( int controller_id, ControllerData data );
    public static native boolean getData( int controller_id, int index_back, ControllerData all_data );
    public static native int getHistorySize();
    public static native boolean setFilterEnabled( boolean on_or_off );
    public static native boolean getFilterEnabled();
    public static native boolean setFilterParams( float near_range, float near_val, float far_range, float far_val );
    public static native boolean getFilterParams( float[] params );
    public static native boolean triggerVibration( int controller_id, int duration, int pattern_id );
    public static native boolean autoEnableHemisphereTracking( int controller_id );
    public static native boolean setHighPriorityBinding( boolean on_or_off );
    public static native boolean getHighPriorityBinding();
    public static native boolean setBaseColor( int red, int green, int blue );
    public static native boolean getColorBase( int[] colors );
    static {
        System.loadLibrary("Sixense_Java");
    }
}

来自 Sixense_Sixense.cpp 的摘录:

#include <jni.h>
#include <sixense.h>
#include "sixense_Sixense.h"

JNIEXPORT jboolean JNICALL Java_sixense_Sixense_init(JNIEnv *env, jclass obj) {
    int i;
    i = sixenseInit();
    return (i == -1) ? JNI_FALSE : JNI_TRUE;
}

来自 Sixense_Sixense.h 的摘录:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class sixense_Sixense */

#ifndef _Included_sixense_Sixense
#define _Included_sixense_Sixense
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     sixense_Sixense
 * Method:    init
 * Signature: ()Z
 */
JNIEXPORT jboolean JNICALL Java_sixense_Sixense_init
  (JNIEnv *, jclass);

垃圾箱的输出:

File Type: DLL

  Section contains the following exports for Sixense_Java.dll

    00000000 characteristics
    4FFBC481 time date stamp Tue Jul 10 01:58:25 2012
        0.00 version
           1 ordinal base
          23 number of functions
          23 number of names

    ordinal hint RVA      name

          1    0 00001D10 Java_sixense_Sixense_autoEnableHemisphereTracking@12
          2    1 000011A0 Java_sixense_Sixense_exit@8
          3    2 00001A40 Java_sixense_Sixense_getAllData@16
          4    3 000019A0 Java_sixense_Sixense_getAllNewestData@12
          5    4 00001DC0 Java_sixense_Sixense_getColorBase@12
          6    5 00001B30 Java_sixense_Sixense_getData@20
          7    6 00001BD0 Java_sixense_Sixense_getFilterEnabled@8
          8    7 00001C40 Java_sixense_Sixense_getFilterParams@12
          9    8 00001D60 Java_sixense_Sixense_getHighPriorityBinding@8
         10    9 00001B90 Java_sixense_Sixense_getHistorySize@8
         11    A 000011C0 Java_sixense_Sixense_getMaxBases@8
         12    B 00001210 Java_sixense_Sixense_getMaxControllers@8
         13    C 00001AE0 Java_sixense_Sixense_getNewestData@16
         14    D 00001220 Java_sixense_Sixense_getNumActiveControllers@8
         15    E 00001180 Java_sixense_Sixense_init@8
         16    F 000011F0 Java_sixense_Sixense_isBaseConnected@12
         17   10 00001230 Java_sixense_Sixense_isControllerEnabled@12
         18   11 000011D0 Java_sixense_Sixense_setActiveBase@12
         19   12 00001D90 Java_sixense_Sixense_setBaseColor@20
         20   13 00001BA0 Java_sixense_Sixense_setFilterEnabled@12
         21   14 00001C00 Java_sixense_Sixense_setFilterParams@24
         22   15 00001D30 Java_sixense_Sixense_setHighPriorityBinding@12
         23   16 00001CE0 Java_sixense_Sixense_triggerVibration@20

  Summary

        1000 .bss
        1000 .data
        1000 .edata
        1000 .idata
        1000 .rdata
        1000 .reloc
        2000 .text

【问题讨论】:

  • 您的 -Djava.library.path VM arg 中有包含 dll 的文件夹吗?
  • dumpbin /exports Sixense_Java.dll 报告什么?
  • DLL 位于 java.library.path 和 @Petesh 的一个文件夹中,我刚刚添加了 dumpbin 的输出。

标签: java dll java-native-interface


【解决方案1】:

在 Java 中调用本机函数时,运行时会尝试使用:

  • 一个标准调用风格的修饰函数:_functionName@n,其中 n 是参数列表中的字节数,
  • 和一个 cdecl 风格的未修饰函数:functionName

使用 MinGW 编译时,您会得到这两者的混合:MinGW 将附加 @n,但不会附加下划线:functionName@n

解决这个问题的方法是告诉ld不要添加后缀,使用--kill-at开关(或者-Wl,--kill-at,如果你通过GCC调用它)。

【讨论】:

  • 您能否更深入地回答您的问题?我遇到了与 OP 相同的问题,我使用 makefile 调用 javah 来生成标头和 g++ 来生成 .o 和 .dll。我正在使用 MinGW,但 @n 没有附加到任何函数名称,也没有附加下划线,尽管与 OP 不同,只是修改文件(我知道,真的不应该这样做)实际上并没有帮助。
  • 为了最大的兼容性,也可以指定--add-stdcall-alias。使用 CMake,这可以通过 set_target_properties (target PROPERTIES LINK_FLAGS "-Wl,--add-stdcall-alias") 实现。
【解决方案2】:

我找到了答案!似乎在 Windows 中使用 JNI 时,它会查找以 _Java_ 开头的函数,而在其他所有平台中,它都会查找 Java_。为什么会这样,而不是写在我不知道的文档中,但它使一切工作完美!

【讨论】:

  • 那么你是如何修复它的呢?更改了 dll 编译标志?
  • javah 创建头文件时,每个函数的名称都类似于Java_package_Class_method。奇怪的是,当像这样编译时,JNI 找不到适合本机方法的函数并吐出错误,但如果在 Java 之前添加下划线,那么 JNI 将能够找到该函数。附带说明一下,我已经尝试使用 VC++2010 进行编译(我之前使用过 MinGW),当时编译时下划线是一个问题。去图吧。
  • 前导下划线是 MSVC 的已知 stdcall 名称修饰。只要您针对同一个JDK(jni.h是关键)进行编译和使用,就没有问题。另一方面,MinGW 没有这样的装饰,也不指望它。您可能以某种方式混淆了这两种环境或调用约定。胡乱猜测。但是破解您创建的标头不是解决方案。 “请勿编辑此文件”的评论确实是有原因的。
【解决方案3】:
java.lang.UnsatisfiedLinkError: no Sixense_Java in java.library.path

Java 好像找不到库,你放对了吗?

您可能需要更改java.library.path,看看this

如果你想打印它的内容,请使用:

System.out.println( System.getProperties().getProperty("java.library.path"));

这可能会打印很多,您可能想split(";") 并单独打印每个条目。

【讨论】:

  • 只有在我删除库以测试它是否发送了不同的错误消息时才输出该错误。
  • 我刚刚注意到 Sixense_init 请求两个参数,但您在没有它们的情况下调用它。这可能是找不到函数的原因。
  • JNI 的工作方式是向每个本机函数发送 JNIEnv 和对调用该函数的类的引用以及任何附加参数。 JNI 自己负责发送 JNIEnv 和类引用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-08-25
  • 1970-01-01
  • 2015-07-31
  • 1970-01-01
  • 2014-05-29
  • 1970-01-01
相关资源
最近更新 更多