Eclipse Java JNI,java.lang.UnsatisfiedLinkError 加载 dll

Ecplise Java JNI, java.lang.UnsatisfiedLinkError loading dll

我在加载打印机 dll 时遇到问题。我有打印机制造商 (JniPrinterStatusLib.dll) 提供的 dll 文件。我按照打印机制造商的建议编写了代码。代码是:

package com.printer.test

public class JniPrinterStatus {
    static{
        System.loadLibrary("JniPrinterStatusLib");
    }

    public native int GetStatus(String printer);
}
package com.printer.test

public class TestSample {
    public static void main(String[] args) {
        int status;
        String printer = "MY PRINTER";
        JniPrinterStatus jps = new JniPrinterStatus();

        System.out.println("PRINTER NAME = " + printer);

        status = jps.GetStatus(printer);
        if (status == -1) {
            System.out.println("status = -1");
        }
        else if (status == 0) {
            System.out.println("status = NORMAL");
        }
        else if ((status & 0x00000080) != 0) {
            System.out.println("status = PRINTER_STATUS_OFFLINE");
        }
        else if ((status & 0x00400000) != 0) {
            System.out.println("status = PRINTER_STATUS_DOOR_OPEN");
        }
        else if ((status & 0x00000010) != 0) {
            System.out.println("status = PRINTER_STATUS_PAPER_OUT");
        }
        else if ((status & 0x00000800) != 0) {
            System.out.println("status = PRINTER_STATUS_OUTPUT_BIN_FULL");
        }
        else if ((status & 0x00000040) != 0) {
            System.out.println("status = PRINTER_STATUS_PAPER_PROBLEM");
        }
   }
}

我用Eclipse来运行代码,我把dll库放在文件夹project里面,报错

PRINTER NAME = MY PRINTER
Exception in thread "main" java.lang.UnsatisfiedLinkError: com.printer.test.JniPrinterStatus.GetStatus(Ljava/lang/String;)I
    at com.printer.test.JniPrinterStatus.GetStatus(Native Method)
    at com.printer.test.TestSample.main(TestSample.java:10)

如果我将源代码从包 "com.printer.test" 移至默认包,代码将运行并显示:

PRINTER NAME = MY PRINTER
status = -1

我不知道这怎么可能。如果我编译并 运行 命令提示符下的代码,而没有包,它就可以工作。

问题出在哪里?

谢谢

抱歉,其实我想写评论,但由于我的声誉仍然很低,所以我必须尝试猜测一个答案。

  • 应该不需要重新编译 dll - 它只是一些要调用的本机代码。
  • class加载dll的java包应该也没有什么区别。

您必须注意您的系统架构:64 位 dll 文件在 32 位 JRE 中会失败,反之亦然。确保您的 JRE 架构与 dll 架构匹配。

另一件需要考虑的事情是你的工作目录。 Eclipse 使用的工作目录可能与您 运行 从控制台编程时使用的工作目录不同。

最后但同样重要的是,请查看您的 java.library.path 变量。

此页面也可能有帮助:https://www.chilkatsoft.com/java-loadLibrary-Windows.asp 我涵盖了所有细节。

来自 javadoc class UnsatisfiedLinkError...

Thrown if the Java Virtual Machine cannot find an appropriate native-language definition of a method declared native.

这意味着找不到函数 Java_com_printer_test_JniPrinterStatus_GetStatus

classjava.lang.System中的方法loadLibrary通常搜索[系统]属性"java.library.path"中列出的目录。对于Windows机器,这个属性的值一般是PATH环境变量的值

所以我建议在您的代码中打印出 属性 的值,以查看它是否包含包含您的 DLL 的目录。如果不是,那么您需要通过重新定位 DLL 或更改 PATH 环境变量或使用 -Djava.library.path=... 选项启动 java 程序来修复该问题。之后,您需要检查本机方法的签名。 Dependency Walker 是我在工作中用来完成此任务的工具。

编辑 有re-read你的问题,我觉得我没有准确回答你的问题,所以让我补充...

Eclipse 的默认行为是将资源文件(如 DLL)复制到输出文件夹。因此,如果您将 DLL 放在文件夹 src\com\printer\test 中,它将复制到文件夹 bin\com\printer\test。我的猜测是当前的工作目录,即 . 在你的 "java.library.path" 中,这就是当你的 java 代码在默认包中时它起作用的原因。

Java classes 的预期包是 JNI 库中的 hard-coded。在您的情况下,它是默认包。

让我详细说明一下。当在 JNI 库中实现本机方法时,必须创建一个 public C 函数,其名称格式如下:

Java_com_mypackage_MyClass_MyMethod

换句话说,JNI 库无法为任意包中的 classes 提供方法 - 只能为 JNI 库作者考虑的包中的 classes 提供方法。

在您的情况下,这是默认设置。 C 函数变为 Java_JniPrinterStatus_GetStatus。如果您调用 class MyPrinterStatus,或将其放入包 com.foobar,JNI run-time 将无法将 C 函数与声明的 [=24= 相关联] 本机方法。这就是 JNI 的设计方式。