Tengo un problema al cargar la impresora dll. Tengo un archivo dll del fabricante de la impresora (JniPrinterStatusLib.dll). Escribí código como sugirió el fabricante de la impresora. El codigo es:

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");
        }
   }
}

Usé Eclipse para ejecutar el código, puse la biblioteca dll en el proyecto de carpeta y el error es

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)

Si muevo la fuente del paquete "com.printer.test" al paquete predeterminado, el código funciona y muestra:

PRINTER NAME = MY PRINTER
status = -1

No se como es posible. Si compilo y ejecuto el código desde el símbolo del sistema sin paquete, funciona.

¿Dónde está el problema?

Gracias

0
Soul85 9 may. 2019 a las 16:03

3 respuestas

La mejor respuesta

Desde el javadoc para la clase UnsatisfiedLinkError ...

Se genera si la máquina virtual Java no puede encontrar una definición de idioma nativo apropiada de un método declarado nativo.

Eso significa que la función Java_com_printer_test_JniPrinterStatus_GetStatus no se encuentra.

El método loadLibrary en la clase java.lang.System generalmente busca los directorios listados en la propiedad [System] "java.library.path". Para máquinas Windows, el valor de esta propiedad es generalmente el valor de la variable de entorno PATH.

Por lo tanto, sugiero imprimir el valor de esa propiedad en su código para ver si incluye el directorio que contiene su DLL. Si no es así, debe solucionarlo, ya sea reubicando la DLL o cambiando la variable de entorno PATH o iniciando su programa java con la opción -Djava.library.path=.... Después de eso, debe verificar la firma del método nativo. Dependency Walker es una herramienta que utilizo en mi trabajo para lograr esto.

EDITAR Después de volver a leer su pregunta, siento que no respondí con precisión su pregunta, así que permítanme agregar ...

El comportamiento predeterminado de Eclipse es copiar archivos de recursos, como DLL, en la carpeta de salida. Entonces, si coloca su DLL en la carpeta src\com\printer\test, se copiará en la carpeta bin\com\printer\test. Supongo que el directorio de trabajo actual, es decir, . está en su "java.library.path", razón por la cual funciona cuando su código java está en el paquete predeterminado.

0
Abra 10 may. 2019 a las 07:11

El paquete esperado de las clases Java está codificado en la biblioteca JNI. En su caso, es el paquete predeterminado.

Déjame ampliar sobre eso. Cuando uno implementa un método nativo en una biblioteca JNI, tiene que crear una función C pública con un nombre en el siguiente formato:

Java_com_mypackage_MyClass_MyMethod

En otras palabras, la biblioteca JNI no puede proporcionar métodos para las clases en paquetes arbitrarios, solo para clases en paquetes que los autores de la biblioteca JNI tenían en mente.

En su caso, es el predeterminado. La función C va Java_JniPrinterStatus_GetStatus. Si llama a su clase MyPrinterStatus, o la coloca en el paquete com.foobar, el tiempo de ejecución JNI no podrá asociar la función C con el método nativo Java declarado. Así es como se diseñó JNI.

0
Seva Alekseyev 10 may. 2019 a las 16:12

Lo siento, en realidad quería escribir un comentario, pero como todavía tengo poca reputación, tengo que intentar adivinar una respuesta.

  • No debería haber necesidad de recompilar el dll, solo se debe invocar un código nativo.
  • El paquete java de la clase que carga el dll tampoco debería marcar la diferencia.

Debe tener cuidado con la arquitectura de su sistema: un archivo dll de 64 bits fallará en un JRE de 32 bits y viceversa. Asegúrese de que su arquitectura JRE coincida con la arquitectura dll.

Otra cosa a tener en cuenta es su directorio de trabajo. Eclipse puede usar un directorio de trabajo diferente al que usó cuando ejecutó su programa desde la consola.

Por último, pero no menos importante, eche un vistazo a su variable java.library.path.

Esta página también puede ayudar: https://www.chilkatsoft.com/java-loadLibrary- Windows.asp Cubre todos los detalles.

0
Jochen Reinhardt 10 may. 2019 a las 06:58