Veamos un ejemplo hola mundo escrito en consola por la función printf de la librería msvcrt si la plataforma es windows o la librería c en caso contrario:
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;
public class HelloWorld {
public interface CLibrary extends Library {
CLibrary INSTANCE = (CLibrary)Native.loadLibrary(
(Platform.isWindows() ? "msvcrt" : "c"),
CLibrary.class);
void printf(String format, Object... args);
}
public static void main(String[] args) {
CLibrary.INSTANCE.printf("Hola mundo!\n");
for (int i=0;i < args.length;i++) {
CLibrary.INSTANCE.printf(
"Argumento %d: %s\n", i, args[i]);
}
}
}
Pueden apreciar la simpleza con la que se "wrappea" la funcion printf de c. Noten que además de imprimir el clásico "hola mundo" se imprimen todos los argumentos pasados al metodo main utilizando el formateo que nos ofrece la funcion printf de c.
Esto es solo un ejemplo ilustrativo de uso, pero imaginen el potencial de facilidad que nos permite JNA al utilizar funciones de c en java comparando con el arduo trabajo que nos llevaría con JNI.
Llevo un tiempo utilizando JNA y aunque me he encontrado con un par de inconvenientes, pude dar resolución feliz a todo lo que quise "wrappear" a java desde librerías nativas. Hablo de invocar funciones pasando como parametros estructuras complejas, y parámetros por referencia para poder obtener retornos. Hasta ahora todo lo pude hacer y de forma simple. :)
Para finalizar, algunos proyectos que utilizan JNA:
- JRuby (Charles Nutter)
- Rococoa Java bindings to the Mac OS X Cocoa framework.
- Freedom for Media in Java (FMJ) (Ken Larson/Dieter Krachtus)
- gstreamer for Java (Wayne Meissner)
- Videolan JVLC Java Multimedia Library
- SVNKit pure Java Subversion client library.
- OmegaT Computer-Aided Translation, which makes use of the hunspell spell checker.
- IntelliJ IDEA (JetBrains)
- NetBeans IDE
- Drive Lego Mindstorm NXT (Emmanuel Pirsch)
- Miscellaneous contributed mappings and utilities (including Windows NT Service, Registry, and User Account libraries).
- A JavaFX Clock (Augusto Sellhorn)
- Detect User Inactivity (Olivier Chafik)
- Athena Backup (Doug Patriarche)
- USB for Java (Mario Boikov)
Cualquier consulta sobre JNA u cualquier otra tecnología la pueden realizar en:
Yo estoy viendo una interface que carga estaticamente (creo) una variable INSTANCE que luego se utiliza para llamar a la funcion printf que esta definida en dicha interface.
ResponderEliminar1 - La pregunta es, como hace JNA para llamar a la funcion printf de C (por ejemplo) por atras?
Hace una especie de Reflection entre C y Java para inspeccionar los metodos que expone esta libreria y si alguno matchea con el que defino en la interface entonces la llamo?
2 - ¿Que pasa si pongo una firma de metodo en esta interface que no matchea con nada que haya en la libreria?
3 - ¿Como hace para matchear el "Object..." en C?
Bueno, vos querias que comentara... jejejeje
Hola martosoler.
ResponderEliminarEfectivamente INSTANCE es una variable publica, estatica y final (recordemos que no pueden existir variables de instancia en una interface) que nos provee una representacion de la libreria en la que se encuentra la funcion que deseamos "mapear" a java.
1- JNA utiliza por atras JNI. JNI es el "sistema basico" que posee java para comunicarse con codigo nativo. JNA solo nos proporciona una capa de abstraccion del lado de java para no tener que lidiar con JNI en c o c++.
2- Hay reglas que nos indica la api de JNA de como mapear correctamente argumentos. Por ejemplo el nombre de la funcion, debe ser identico al metodo en la interface. un argumento unsigned char *, se mapea en jna como String, un int *, como un objeto IntByReference del api de JNA, estructuras como objetos que implementan la interface Structure de JNA, etc. Si mapeas mal el nombre te arrojara una simpatica excepcion. :)
3- Hay que destripar el codigo fuente. JNA es Open Source!!! :D
Gracias por tu comentario!
Se ve picante... !!!
ResponderEliminarGracias por la respuesta
Hola Ale Vouilloz,
ResponderEliminarEstoy intetando utilizar jna para acceder a las funciones de una dll, pero estoy teniendo inconvenientes con una de éstas...
La función me recive un char por referencia y un int, por defecto la función me retorna un int que corresponde al resultado de la ejecución, es decir, si fué o no fué exitosa... el problema es que no sé cómo acceder al char pasado por referencia pues necesito la información modificada en la función..
Podrías darme una mano?
Por lo que dices, deseas obtener una cadena como parametro de retorno de una funcion nativa.
ResponderEliminarSi la funcion se parece a esto:
int getString(char* cadena, int un_entero);
Con JNA deberías tener mapeada la funcion como:
int getString(String cadena, int un_entero);
Donde el String fue inicializado previamente.
Si este no es el caso, pasame mas detalles para que pueda ayudarte.
Gracias por tu respuesta...
ResponderEliminarEfectivamente la función que necesito tiene esa estructura, y de hecho así la estoy mapeando, el problema es que el int de retorno de la función está fuera de los valores posibles, (es por esto que no entiendo que pueda estar sucediendo), además cuando intento ver el string modificado por la función, este no ha cambiado...
Ahora te especifico los detalles:
la función en c es:
ATOpenSensor (TCHAR* pszStrSensorName, int16 iAccessMode )
la tengo mapeada en JNA así:
int ATOpenSensorA(String nombre, int tipo);
Nota: en la dll se encuentra con este nombre ATOpenSensorA y hay otra ATOpenSensorW;
al llamarla
int retorno = ATOpenSensorA("primero", 1);
retorno debería ser 0 para verificar que la función se ejecutó bien, o unos valores entre negativos y positivos pero con no mas de 3 cifras, el problema es que el valor que retorna es de 5 cifras!!!...
Gracias de antemano...
Ten en cuenta que el tipo int en Java esta representado en 32 bit de tamaño. Si la funcion espera un tipo int16 como argumento deberias usar el tipo short de java que es de 16 bit. Esa es una posibilidad que dentro de la funcion se produzca un error al leer el entero y devolverte asi un codigo de error distinto de cero.
ResponderEliminarEn cuanto al parametro de tipo TCHAR* prueba mapearlo como byte[], ya que el String en java es inmutable y es correcto mapearlo con const char* de c. Prueba:
byte[] nombre = new byte[100];
int ATOpenSensorA(byte[] nombre, short tipo);
String nombreStr = Native.toString(nombre);
Espero que esta vez funcione. Suerte! :D
Gracias nuevamente.. Misión Exitosa!!
ResponderEliminarEl cambio del int a short me sirvió para retornar el valor verdadero de retorno y además para que la función se ejecute correctamente..
Muchas gracias Ale Vouilloz!!!
luego te molestaré con paso de estructuras por referencia XD ....
Hola Ale Vouilloz
ResponderEliminarPodrías ayudarme con el siguiente código de C, tengo entendido que es un callBack pero la verdad no he encontrado lo que necesito.
En https://jna.dev.java.net encontre algunos ejemplos del mapeo a jna, pero la verdad no estoy muy familiarizado con las callback
éste es el codigo de C
typedef void (*AT_NOTIFY_CALLBACK)(void*, tsAT_API_MSG*);
y más adelante se define como parámetro de una función de la dll...
ATBeginAcquireImage (
AT_NOTIFY_CALLBACK pCallbackProc,
void* pCallerParam,
uint16 iOperationType,
void* pReferenceTemplate)
Hola Ale,
ResponderEliminarestoy desarrollando un programa de facturación en Java, pero me encuentro trabado en el tema de la impresora fiscal. Desde Epson me mandaron una libreria ".lib" hecha en C++ y no puedo ver el codigo. Estoy teniendo problemas para hacer la conexion entre codigos, ya que todos los ejemplos que encuentro son para librerias dinamicas y no estaticas.
Buscando me tope con tu pagina y me llamo la atencion lo de JNA.
Ahora la pregunta: crees que puede serme util para hacer la conexion el JNA? como lo podria llegar a aplicar mediante un ejemplo?
Desde ya muchas gracias
Hola Anónimo:
ResponderEliminarJNA está pensado para ser usado con librerías dinámicas (.dll en Windows). Si lo que tienes en manos es una librería estática de Windows (.lib) y quieres usarla desde Java, tienes éstas opciones:
1- Convencer a la gente de Epson para que te envien la libreria compilada como una librería dinamica dll y así poder mapear las funciones con JNA.
2- Crear una librería Wrapper con JNI para poder luego desde java invocar al wrapper. Aquí debes poseer más conocimientos sobre punteros en C o C++
Suerte!
Gracias Ale, consegui la 1era de las opciones que me planteaste(por suerte). Ahora voy a hacer el intento mediante JNA con la libreria dinamica en mis manos.
ResponderEliminarMuchas gracias por responder
Saludos
Hola Ale,
ResponderEliminarTengo un problema, ojala puedas ayudarme.
Se me presenta la siguiente linea:
dword AddMessageField( byte* uchField, dword iFieldLength );
Como sería el equivalente en Java del tipo "byte*". Estoy probando con String,pero no estoy obteniendo buenos resultados.
Desde ya muchas gracias
Hola Seba.
ResponderEliminarPrueba mapear el argumento de tipo byte* en java como byte[]
Luego, si lo que devuelve la función AddMessageField sabes que es un String para obtener el resultado puedes hacer:
String nombreStr = Native.toString(uchField);
Para más info puedes visitar la web de JNA: https://jna.dev.java.net/
o su javadoc: https://jna.dev.java.net/nonav/javadoc/index.html
Perdón, por no dedicarme más a tu caso, es que ando corto de tiempo.
Suerte!
Hola Ale Vouilloz
ResponderEliminarYa pude solucionar el problema del callback y he avanzado en mi proyecto...
Sin embargo aún tengo algunas dudas respecto al uso de jna. Lo que sucede es que algunas veces las rutinas implementadas para la aplicación, no responden al comando, e incluso se bloquea la aplicación sin que se generen errores en la consola de java.
Lo curioso es que sucede de una manera secuencial la mayoria de las veces....
Alguna idea de lo que puede estar sucediendo?
gracias por la ayuda
Hola Joadarpe:
ResponderEliminarQue bueno que hayas solucionado el problema del Callback, perdona por no poder ayudarte ya que ultimamente no ando que tiempo disponible, tanto asi, que ya que tengo "freezado" el blog en articulos nuevos.
Te cuento que yo he tenido problemas similares con JNA donde la JVM se caia y se debian a dos tipos de problemas:
1- En las librerias nativas existian asignaciones de memoria que no eran correctamente liberadas por lo que se producían "memory leaks", provocando en algun momento aleatorioa la caida de la JVM, esto me sucedía sólo con las pruebas en Linux. Se solucionó liberando las variables con free(), claro yo disponia del código fuente de las librerías.
2- Desde Java, estaba intentando acceder por punteros a variables que desde la parte nativa habian sido liberadas.
Por lo que pude apreciar, las caidas abruptas de la JVM se debe a problemas de asignaciones de memoria. Existe un método estático en JNA que previene éstas caidas: Native.setProtected(true), de todas formas ésto no corrige el problema.
Saludos.
Que tal Ale Vouilloz,
ResponderEliminarEsplendido tu ejemplo, me sirvio de mucho, sin embargo tengo un error, tal vez podrias ayudarme, cuando trato de invocar metodos de C desde unix version aix 5, me presenta el siguiente error: Exception in thread "main" java.lang.UnsatisfiedLinkError: jnidispatch (/com/sun/jna/aix-ppc/libjnidispatch.a) not found in resource path
at com.sun.jna.Native.loadNativeLibraryFromJar(Native.java:592)
esto ocurre presumo que es porque no esta el jnidispatch.a en el JNA.jar, sin embargo no encuentro ningun .jar desde la pagina de Sun que tenga este paquete para esta version de unix. que me sugieres??? Alvaro Perez. Gracias
Hola Ale,
ResponderEliminarEscribo para preguntarte si no hay algún documento/libro o batería de ejemplos para aprender. He buscando incansablemente y los ejemplos que he encontrado son, o muy básicos o muy complejos. No he visto un término medio que sirva de ayuda.
Muchas gracias,
MiguelGT
Alvaro Perez:
ResponderEliminarEvidentemente como dices JNA no puede encontrar la libreria jnidispatch para tu plataforma dentro del jna.jar
En la distribucion que uso no encuentro que sea soportada la plataforma Unix/Aix. Es muy posible que aun no sea soportada. Lamento no poder ayudarte. Si encuentras la solución te invito a que la compartas. Saludos.
Anonimo:
Es verdad que la documentación que anda dando vueltas por la web no es mucho mas de lo que hay en https://jna.dev.java.net/
Mi experiecia fue a base de prueba y error, de igual forma fue productivo ya tenia que usarlo para mapear muchas funciones de muchas librerias.
Pronto escribire un articulo con todo lo que he usado de JNA, Mapeos de parametros Struct, parametros por referencia,etc. Me voy a saltear los callbacks ya no tuve la necesidad de usarlos y mi tiempo libre es escaso como para aprenderlo.
Gracias. Alvaro Perez
ResponderEliminarHola Ale, muy bueno el articulo, lei en uno de los comentarios anteriores que te preguntaban sobre como comunicar java con una impresora fiscal, acabo de terminar de desarrollar una aplicacion de facturacion WEB, y lo unico que me esta faltando es poder generar la factura en la impresora fiscal, mi idea es armar algo generico que pueda adaptar a los diferentes modelos de impresoras, pero hasta ahora no he conseguido ni empezar, te pido a vos o a cualquier que tenga alguna info que me pueda ayudar estare mas que agradecido. gracias
ResponderEliminarhola.... tengo el mismo problemita..... estoy leyendo aquí pero creo que nadie tiene una potencial solución.
Eliminargracias al que me guié con el asunto.
oye tengo un problema donde pongo el archivo jna.jar para poder ocuparlo ya intente de todo
ResponderEliminarOye, pues tan simple como ponerlo en el classpath. Si usas un ide como eclipse o netbeans ésto es muy fácil.
ResponderEliminarHola ale, me podes orientar un poco para conectar una impresora fiscal epson usando la libreria ocx. Se puede acceder con jna?
ResponderEliminarMuchas Gracias
Saludos
Pablo
hola.... tengo el mismo problemita..... estoy leyendo aquí pero creo que nadie tiene una potencial solución.
Eliminargracias al que me guie con el asunto.
ola una consulta, quiero que mi metodo me devuelva un cadena, como hago?
ResponderEliminarHola Ale,
ResponderEliminartengo una duda... a ver si me la puedes responder. Queriq saber cual es el "wrapper" Java para esta función de C++:
void testReturnReference(int iIn, double &dOut)
donde dOut es un parámetro de salida que se le asigna un valor dentro del método. Eso para Java sería el return del método? O estaría tambien en parametros de entrada?
Gracias
Hola
ResponderEliminarMi problema es el siguiente, necesito usar funciones de msi.dll para obtener informacion sobre los programas instalados en una pc. el problema es que nunca he trabajado con JNA y no he encontrado tutoriales para pòder entender la filisofía. Si pudieras ser tan amable de ponerme un codigo de como utilizar las funciones del dll estaría muy agradecido.
Saludos Ernesto Luis
Hola Ale,
ResponderEliminarEstoy tratando de invocar los métodos de una dll pero me tira este error:
Exception in thread "main" java.lang.UnsatisfiedLinkError: Error looking up function 'LG_Open': No se encontró el proceso especificado.
at com.sun.jna.Function.(Function.java:126)
at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:219)
at com.sun.jna.Library$Handler.invoke(Library.java:191)
at $Proxy0.LG_Open(Unknown Source)
at Test_GD300i_SUBE.ventanaTest.main(ventanaTest.java:2069)
El código en c++ de la dll es:
en el archivo .h
extern "C" int LG_Open( int iComm );
en el cpp
int LG_Open( int iComm )
{ // Cierro puerto de comunicaciones
LG_Close( );
// Abro puerto Comm
if ( COM_Open( iComm ) == COM_FAIL )
{ // Retorno con error
return COM_GetLastError( );
}
return LG_OK;
}
el código en aplicación java es el siguiente:
public interface GD300i extends Library {
public int LG_Open(int iComm);
}
//application start
public static void main(String[] args) {
GD300i lib = (GD300i) Native.loadLibrary("GD300i", GD300i.class);
int iRet = 0;
iRet = lib.LG_Open(3);
System.out.println(iRet);
}
Te agradecería tu comentario respecto a que estoy haciendo mal, pues ya le he dado vueltas al asunto y no encuentro resultado satisfactorio.
Muchas gracias, saludos.
Hola anonimo, aparentemente está todo bien.
ResponderEliminar¿Es posible que la función tenga una macro definida en el header y por eso no se encuentra cuando se invoca desde Java?
Perdonen a todos por mi falta de tiempo y responder sus consultas.
Pueden escribirle a Timothy Wall en Markmail que el les sabrá responder mejor sobre éste api.
Gracias por tu pronta respuesta voy a revisar el header para verificar.
ResponderEliminarSaludos.
hola como se soluciona el error de que no se encuentra jnidispatch en win32-x86????
ResponderEliminarGracias antemano
Hola, me han proporcionado una DLL que exporta los nombres de sus funciones de la forma _nombreFuncion@3
ResponderEliminarPartiendo de ejemplos como
public interface MiLibreria extends Library {
int metodo(parametroA,parametroB);
}
Como puedo hacer que me acepte nombres de métodos que tienen dicha forma _nombre@3 ya que de lo contrario obtengo el UnsatisfiedLinkError
De antemano, gracias.
Hola anónimo.
ResponderEliminarAunque no estoy al tanto del progreso actual del proyecto, creo que con JNA no podrás referenciar esas funciones ya que Java no admite el caracter '@' como parte de un identificador.
Hola Ale Vouilloz, soy de nuevo el de las funciones con '@'.
ResponderEliminarPrimero para agradecer la información de éste POST y la pronta respuesta.
Como segundo punto, es para compartir con los usuarios por si alguno tiene el mismo problema, buscando encontré que se soluciona así:
import com.sun.jna.Native;
import com.sun.jna.NativeLibrary;
import com.sun.jna.Pointer;
class CallFooPortably
{
static Map options;
static
{
options = new HashMap();
options.put(Library.OPTION_FUNCTION_MAPPER,
new FunctionMapper()
{
public String getFunctionName(NativeLibrary lib,
Method method)
{
if (method.getName().equals("MIMETODO"))
return "_METODOCON@1";
else
return method.getName();
}
});
}
public interface CLibrary extends Library
{
CLibrary INSTANCE = (CLibrary) Native.loadLibrary("LA_DLL_A_USAR", CLibrary.class,
options);
void MIMETODO();
}
public static void main(String[] args)
{
CLibrary.INSTANCE.MIMETODO();
}
}
Saludos y a programar!
Excelente Joel! gracias por compartir la info!
ResponderEliminarEstoy comenzando a trabajar con JNA y me ha surgido una duda. Estoy tratando de conectar con una dll hecha por mi pero no se como especificar en java la direccion de la dll, no se si copiarla donde está el .class igual que con JNI o si la tengo que poner en otro lado
ResponderEliminarDebes ubicar la dll en un directorio del libraryPath de Java o setear el parametro de inicio de la JVM con -Djava.library.path=./tuDirConLaDll
ResponderEliminarSaludos.
Ale Vouilloz.
Gracias por la pronta respuesta, ya hice lo que me dijiste y avance algo o al menos me cambio la excepcion ahora me pone:
ResponderEliminarException in thread "AWT-EventQueue-0" java.lang.UnsatisfiedLinkError: Unable to load library 'SobelFilterC': No se puede encontrar el módulo especificado.
at com.sun.jna.NativeLibrary.loadLibrary(NativeLibrary.java:163)
at com.sun.jna.NativeLibrary.getInstance(NativeLibrary.java:236)
at com.sun.jna.Library$Handler.(Library.java:140)
at com.sun.jna.Native.loadLibrary(Native.java:379)
at com.sun.jna.Native.loadLibrary(Native.java:364)
la verdad es que ya no se que hacer.
saludos Eduardo
Seguramente la librería que estas queriendo cargar tiene una dependecia a otra librería que no es encontrada. Puedes usar el depends para analizar las dependecias de tu libreria y ubicar las mismas en el mismo directorio del libraryPath de Java que usas.
ResponderEliminarSaludos.
Ale Vouilloz.
Buenas a tod@s menos mal que he podido encontrar este foro!!, ya que nunca había implementado JNA, para utilizar metodos de C a Java.
ResponderEliminarLo de TCHAR *var a JAVA es correcto, lo he probado con lo que comentaba Ale Vouilloz, ya que tenía la misma situación, que no sabía como hacerlo
Gracias a todos,
Buenas una pregunta, como podría hacer un System.out.println() con formato para este tipo de formato que se puede sacar en C printf("0x%081X",mask);
ResponderEliminarGracias
Solucionado, con un System.out.printf("0x%081X",mask);
EliminarBuenas a tod@s,
ResponderEliminarMe surge una duda, tengo el siguiente codigo en C:
typedef enum{
MASK_PIN_FULL= 0x0001, MASK_CONFIG= 0x0002
}
Como lo puedo pasar a JNA??, directamente lo he puesto como:
public final static int MASK_PIN_FULL =0x0001;
public final static int MASK_CONFIG= 0x0002;
Pero mi duda es que si por lo que sea se cambia algún dia esa definición y no me acuerdo de cambiarlo en el código la puedo liar, entonces de que forma sencilla se puede hacer?
Gracias
Creo que lo he solucionado con un ejemplo que habeis puesto y no lo había visto.
EliminarP.D.
Este foro esta muy calmado, no?
Hola Dámaso.
EliminarMe alegra que hayas encontrado la solución.
Esta página no pertenece a un foro, si no, a un articulo de mi blog personal. En la actualidad, el trabajo no permite el tiempo suficiente para dedicarme a el. Sin embargo si a alguien le sige siendo de ayuda sigo complacido.
Saludos
Hola Ale, mi pregunta es si podria utilizar esta biblioteca JNA para un programa licenciado (sharewre - freeware)
EliminarHola anónimo. Tengo entendido que si es posible. Esta librería pertenece a los proyectos "en incubadora" de java.net. Si mal no recuerdo en algun momento estuvo a punto de formar parte del jdk.
Eliminarbuenas tardes.... estoy ponchado con la solucion de poder imprimir en Fiscal.... que tema tan jodido... en java le he dado vueltas y vueltas JNI-JNA..... Y PUES NO DOY CON EL CHISTE....
ResponderEliminaralguien sabe sobre este tema... gracias
ing_vmgc@yahoo.es
Este comentario ha sido eliminado por el autor.
ResponderEliminarBuen día, espero puedan ayudarme con este error al llamar a un método que lee parámetros desde un archivo .ini.
ResponderEliminarEl método tiene como parámetro un String (ruta del .ini) y al invocarlo me muesta el siguiente error:
Exception in thread "main" java.lang.Error: Invalid memory access
at com.sun.jna.Native.invokeInt(Native Method)
at com.sun.jna.Function.invoke(Function.java:383)
at com.sun.jna.Function.invoke(Function.java:315)
at com.sun.jna.Library$Handler.invoke(Library.java:212)
at $Proxy0.fiOpenPort(Unknown Source)
at pruebas.Pruebas.main(Pruebas.java:49)
Java Result: 1
GENERACIÓN CORRECTA (total time: 1 second)
Por cierto, el método que trato de invocar tiene por nombre fiOpenPort(String archivo)
ResponderEliminarHola estimado, pudiste resolver el problema. Actualmente me encuentro en una situación similar.
EliminarSaludos Víctor.
Buenas tardes, estoy tratando de utilizar una dll echa en c++ desde java, he intentado de varias maneras y siempre me da error, me comentaron que es por los tipos que usa la funcion, alguien sabe si existe una manera para conseguirlo, les dejo la firma del metodo de la dll, gracias de antemano.
ResponderEliminarResult API metodoC++ (HWND hWnd,
HANDLE hInst,
SHORT Peripheral,
SHORT *hConnect);
Cualquier consulta sobre JNA u otra tecnología la pueden hacer en:
ResponderEliminarhttp://www.ezeiware.com
Ezeiware es mi empresa de desarrollo de software empresarial.