Acerca de:

Este blog contiene los códigos, ejemplos y bases de datos que he usado cuando aprendía acerca de algún tema específico. En lugar de borrarlos (una vez dominado ya el tema), he decidido publicarlos :)

miércoles, 24 de febrero de 2021

Llamando a una dll en C++ desde Java mediante JNI

Ya había escuchado antes acerca de JNI (Java Native Interface) que permite acceder a código en C o C++ desde java, así que ya era hora de probarlo:

Primero, abro mi Visual Studio 2019, elijo Crear un Proyecto->C++->Biblioteca de Vínculos Dinámicos:

Mi proyecto se llama JNISample y está ubicado en D:. Lo que suelo hacer con mis dlls es crear un nuevo archivo de encabezado, en este caso se llamará jniSample.h:


Luego se hace click derecho al nombre del proyeccto, nos vamos a General->C++->Directorios de Inclusión Adicionales, e incluímos las carpetas Include de nuestra instalación de JDK tal y como indica esta web:


En mi caso las rutas son C:\Program Files\Java\jdk1.8.0_231\include y C:\Program Files\Java\jdk1.8.0_231\include\win32

En el archivo dllmain.cpp añadimos la siguiente función que simplemente toma dos cadenas de caracteres y suma sus longitudes:

La explicación del uso de la variable env está en esta web. En resumen es para indicar que se desea copiar la cadena de caracteres enviada por java a la memoria del código nativo en C++, y al final liberar esta memoria.

En el archivo .h se copia la declaración de la función:

Y se compila. El código dentro de los #ifdef es para que el compilador no decore el nombre de la función.

Ahora hay que crear la aplicación java. Yo estoy usando Netbeans 11.1. La aplicación de ejemplo es de tipo Ant y se llama JNISample1; la voy a ubicar dentro de la carpeta del proyecto de C++, JNISample.

Luego copio la dll compilada a la carpeta donde está el proyecto en Netbeans, en mi caso es D:\JNISample\JNISample1:

 

Siguiendo las instrucciones de esta web y esta otra, se añade el código necesario para cargar la dll y usarla desde java, debe quedar así:

 

Si arroja algún error, puede deberse a que no se ha escrito correctamente el nombre de la librería, éste es el nombre del archivo dll sin la extensión. Más información en este enlace.

Es preferible que la función que llama a la dll sea pública, así podremos usar este jar desde otro. Yo prefiero tener un jar intermedio entre la aplicación principal y la dll debido a los pasos que siguen a continuación:

Para enlazar la dll a la aplicación de java se debe crear el verdadero archivo .h con el que se debe recompilar la dll. Esto se hace invocando al ejecutable javah que existe dentro de la instalación del JDK (no he probado con el JRE). Para este paso hay que seguir las instrucciones de esta web, que se resumen en abrir la consola en la ubicación de la dll e ingresar el siguiente comando:

"C:\Program Files\Java\jdk1.8.0_231\bin\javah" -o jniSample.h -jni -classpath "D:\JNISample\JNISample1\build\classes" "jnisample1.JNISample1"

De forma más genérica tiene la siguiente forma:

"ruta a mi instalacion de java donde está javah" -o nombre_de_la_dll.h -jni -classpath "ruta de mi proyecto\build\classes" "paquete.clase"

Al presionar Enter, y si todo sale bien no debe decirnos nada, sólo debe mostrarnos mensajes en caso de un error.

Y con esto ya tenemos nuestro archivo de cabecera .h el cual se debe reemplazar en el proyecto de Visual Studio. En mi caso javah me generó esto:

 

Simplemente copio todo este contenido y lo reemplazo en mi archivo jniSample.h de mi proyecto de Visual Studio.

A continuación se le cambia el nombre a la función original en dllmain.cpp.

Inicialmente estaba así:

Y ahora debo modificarla cambiándole el nombre que aparece en el archivo .h:

Luego compilo todo y reemplazo la dll resultante por la que está en la carpeta del proyecto de Netbeans.

Y eso es todo:


Los dos proyectos se pueden descargar de este enlace. El proyecto de Visual Studio es para la versión 2019.