Исключите Java JNI, java.lang.UnsatisfiedLinkError, загружая dll - PullRequest
0 голосов
/ 09 мая 2019

У меня проблема с загрузкой принтера dll.У меня есть файл DLL от производителя принтера (JniPrinterStatusLib.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 в проект папки, и ошибка:

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

Я не знаю, как это возможно.Если я компилирую и запускаю код из командной строки без пакета, это работает.

В чем проблема?

Спасибо

Ответы [ 3 ]

0 голосов
/ 10 мая 2019

Из javadoc для класса UnsatisfiedLinkError ...

Брошенный, если Виртуальная машина Java не может найти соответствующее определение на родном языке метода, объявленного собственным.

Это означает, что функция Java_com_printer_test_JniPrinterStatus_GetStatus не найдена.

Метод loadLibrary в классе java.lang.System обычно выполняет поиск в каталогах, перечисленных в свойстве [System] "java.library.дорожка".Для машин Windows значение этого свойства обычно является значением переменной среды PATH.

Поэтому я предлагаю распечатать значение этого свойства в вашем коде, чтобы увидеть, включает ли оно каталог, содержащий вашу DLL.Если этого не произойдет, вам нужно будет это исправить, либо переместив DLL, либо изменив переменную среды PATH, либо запустив Java-программу с опцией -Djava.library.path=....После этого вам нужно проверить подпись нативного метода. Dependency Walker - это инструмент, который я использую для своей работы.

РЕДАКТИРОВАТЬ Перечитав ваш вопрос, я чувствую, что не совсем точно ответил на ваш вопрос,поэтому позвольте мне добавить ...

Поведение по умолчанию Eclipse заключается в копировании файлов ресурсов, таких как DLL, в выходную папку.Поэтому, если вы поместите свою DLL в папку src\com\printer\test, она получит копию в папку bin\com\printer\test.Я предполагаю, что текущий рабочий каталог, то есть ., находится в вашем "java.library.path", поэтому он работает, когда ваш код Java находится в пакете по умолчанию.

0 голосов
/ 10 мая 2019

Ожидаемый пакет классов Java жестко запрограммирован в библиотеке JNI. В вашем случае это пакет по умолчанию.

Позвольте мне подробнее остановиться на этом. Когда кто-то реализует собственный метод в библиотеке JNI, он должен создать общедоступную функцию C с именем в следующем формате:

Java_com_mypackage_MyClass_MyMethod

Другими словами, библиотека JNI не может предоставлять методы для классов в произвольных пакетах - только для классов в пакетах, которые имели в виду авторы библиотеки JNI.

В вашем случае это по умолчанию. Функция C идет Java_JniPrinterStatus_GetStatus. Если вы вызываете свой класс MyPrinterStatus или помещаете его в пакет com.foobar, среда выполнения JNI не сможет связать функцию C с объявленным нативным методом Java. Именно так был разработан JNI.

0 голосов
/ 10 мая 2019

Извините, на самом деле я хотел написать комментарий, но, поскольку у меня все еще низкая репутация, я должен попытаться угадать ответ.

  • Не нужно перекомпилировать dll- это просто некоторый нативный код, который нужно вызвать.
  • Пакет java класса, загружающего dll, также не должен иметь значения.

Вы должны позаботиться о своей системеархитектура: 64-битный файл dll не будет работать в 32-битной JRE и наоборот.Убедитесь, что ваша архитектура JRE соответствует архитектуре dll.

Еще одна вещь, которую необходимо учитывать, - это ваш рабочий каталог.Eclipse может использовать рабочий каталог, отличный от того, который вы использовали, когда запускали программу из консоли.

И последнее, но не менее важное, пожалуйста, взгляните на вашу переменную java.library.path.

ЭтоСтраница также может помочь: https://www.chilkatsoft.com/java-loadLibrary-Windows.asp Я охватываю все детали.

...