You are on page 1of 27

Instituto Tecnológico de Culiacán

Ing. en Sistemas
Computacionales
Tópicos Avanzados de Programación

Unidad II: Componentes y Librerias


Librerías dinámicas
Las librería de enlace dinámico (también llamadas DLL,
librerías dinámicas o módulos de librerías) son uno de los
elementos estructurales más importantes de Windows.

Las librerías de enlace dinámico no suelen ejecutarse


directamente. Son archivos independientes que contienen
funciones que pueden ser llamadas por los programas y
por otras DLL para realizar ciertos trabajos.

Una librería de enlace dinámico sólo entra en acción


cuando otro módulo llama a una de las funciones en la
librería.
Librerías dinámicas
El termino “enlace dinámico” (dynamic linking) designa
el proceso que usa Windows para enlazar una llamada de
función en un módulo con la función real en el módulo
de librería.

El “enlace estático” (static linking) se produce durante la


compilación de su programa, cuando usted enlaza varios
módulos objeto (.OBJ), archivos de librería en tiempo de
ejecución (.LIB) y, normalmente, un archivo de recursos
compilados (.RES) para crear un archivo
Windows .EXE.

Por el contrario, el enlace dinámico se produce en


tiempo de ejecución.
Librerías dinámicas
KERNEL32.DLL, USER32.DLL y GDI32.DLL,
los diferentes archivos controladores (por
ejemplo, KEYBOARD.DRV, SYSTEM.DRV y
MOUSE.DRV) y los controladores de vídeo y de
impresora son librerías de enlace dinámico.

Estas son librerías que todos los programas de


Windows pueden usar.
Librerías dinámicas
Algunas librerías de enlace dinámico (por ejemplo, los archivos de fuentes)
se denominan “sólo-recurso”. Contienen sólo datos (normalmente en forma
de recursos) y no tienen código.

Por tanto, un objetivo de las librerías de enlace dinámico es proporcionar


funciones y recursos que se pueden usar con diferentes programas.

En un sistema operativo convencional, sólo el propio sistema operativo


contiene rutinas que otros programas pueden llamar para hacer algún
trabajo.

En Windows, es habitual el proceso de un módulo llamando a una función


en otro módulo. En efecto, escribiendo una librería de enlace dinámico, está
escribiendo una extensión a Windows. O puede pensar en las librerías de
enlace dinámico (incluyendo a aquellas que maquillan a Windows) como
extensiones a su programa.
Librerías dinámicas
Aunque un módulo de librerías de enlace dinámico
puede tener cualquier extensión (por ejemplo, .EXE
o .FON), la extensión estándar en Windows es .DLL.

Sólo se cargan automáticamente con Windows las


librerías de enlace dinámico con la extensión DLL.

Si el archivo tiene otra extensión, el programa tiene que


cargar explícitamente el módulo usando la función
LoadLibrary o LoadLibraryEx.
Librerías dinámicas
Generalmente, encontrará que las librerías de enlace dinámico son más convenientes
en el contexto de una aplicación de gran tamaño.

Por ejemplo, suponga que escribe un complejo programa de contabilidad para


Windows formado por cuatro programas diferentes. Probablemente, estos cuatro
programas usarán algunas librerías comunes. Puede poner estás rutinas comunes en
una librería objeto normal (con la extensión .LIB) y añadirla a cada uno de los
módulos de programa durante el enlace estático con LINK.

Pero este enfoque no es eficiente, pues cada programa contiene código idéntico para
las rutinas comunes. Además, si cambia una de las rutinas en la librería, tendrá que
volver a enlazar todos los programas que hacen uso de la rutina modificada.

Sin embargo, si pone estas rutinas comunes en una librería de enlace dinámico
llamada, por ejemplo, CONTAB.DLL, habrá resuelto ambos problemas. Sólo el
módulo de librería necesita contener las rutinas requeridas por todos los programas
(requiriendo por tanto menos espacio de disco para los archivos y menos espacio de
memoria cuando se ejecutan dos o más aplicaciones simultáneas) y puede hacer
cambios al módulo de librería sin necesidad de volver a enlazar los programas
Librerías dinámicas
Las librerías de enlace dinámico pueden ser ellas mismas
productos definitivos.

Por ejemplo, suponga que escribe una colección de


rutinas de dibujo tridimensionales y las pone en una
librería de enlace dinámico llamada GDI3.DLL.

Si luego logra interesar a otros desarrolladores de


software en utilizar su librería, puede licenciarla para ser
incluida con sus programas gráficos.

Un usuario que tiene varios de estos programas sólo


necesita un archivo GDI3.DLL
Librería: una palabra, varios
significados
Parte de la confusión que acompaña a las librerías de enlace dinámico se
deriva de la aparición de la palabra “librería” en varios contextos diferentes.

Aparte de la librerías de enlace dinámico, también hablaremos de “librerías


objeto” (object libraries) y “librerías de importación” (import libraries).
Librería: una palabra, varios
significados
Una librería “objeto” es un archivo con extensión .LIB que contiene código
que se añade al archivo .EXE de su programa cuando usted ejecuta el
enlazador durante el enlace estático.

Por ejemplo, en Microsoft Visual C++, la librería objeto normal que usted
enlaza con su programa se llama LIBC.LIB.
Librería: una palabra, varios
significados
Una librería de importación es una forma especial de archivo de librería
objeto. Igual que las librerías objeto, las librerías de importación tienen la
extensión .LIB y se utilizan por el enlazador para resolver llamadas a
funciones en su código fuente.

Sin embargo, las librerías de importación no contienen código. En su lugar,


proporcionan al enlazador (linker) la información necesaria para definir
tablas de relocalización dentro del archivo .EXE para enlace dinámico.

Los archivos KERNEL32.LIB, USER32.LIB y GDI32.LIB incluidos en el


compilador de Microsoft son librerías de importación para las funciones
Windows. Si usted llama a Rectangle() en un programa, GDI32.LIB le
indica a LINK que esta función está en la librería de enlace dinámico
GDI32.DLL. Está información va en el archivo .EXE para que Windows
pueda realizar enlace dinámico con la librería de enlace dinámico
GDI32.DLL cuando se ejecute su programa.
Librería: una palabra, varios
significados
Las librerías objeto y las librerías de importación se usan
únicamente durante el desarrollo de programas.

Las librerías de enlace dinámico se usan en tiempo de ejecución.

Cuando Windows necesita cargar un módulo de librería de enlace


dinámico antes de ejecutar un programa que utiliza dicho módulo,
el archivo de librería debe estar almacenado en el directorio que
contiene el programa .EXE, el directorio activo, el directorio de
sistema de Windows, el directorio de Windows o un directorio
accesible a través de la cadena PATH del entorno MS-DOS (Los
directorios se buscan en ese orden).
Una DLL sencilla
Una manera sencilla de comunicar un programa
en Java con una DLL es a través de la interfaz
JNI (Java Native Interfaz) o interfaz de código
nativo en Java.

Nota: El código nativo es aquel que es específico


de un procesador, a diferencia de los codebytes
de Java que son genéricos para todas las
máquinas virtuales de Java.
Una DLL sencilla
Esta ejercicio implementa el consabido programa "Hello World!". Este programa
tiene dos clases Java. La primera, llamada Main, implementa el método main() para
todo el programa. La segunda, llamada HelloWorld, es un método, un método nativo,
que muestra "Hello World!". La implementación para el método nativo se ha
proporcionado en lenguaje C.

Paso 1: Escribir el Código Java


Crea un clase Java llamada HelloWorld que declara un método nativo. También,
escribe el programa principal que crea el objeto HelloWorld y llama al método
nativo.

Paso 2: Compilar el Código Java


Utiliza javac para compilar el código Java escrito en el Paso 1.

Paso 3: Crear el archivo .h


Utiliza javah para crear un archivo de cabecera (un archivo .h) al estilo JNI, a partir
de la clase HelloWorld. El archivo de cabecera proporciona una definición de
función para la implementación del método nativo displayHelloWorld(), que se ha
definido en la clase HelloWorld.
Una DLL sencilla
Paso 4: Escribir la Implementación del Método Nativo
Escribe la implementación para el método nativo en un archivo
fuente en el lenguaje nativo. La implementación será una función
normal que será integrada con nuestra clase Java.

Paso 5: Crear una Librería Compartida


Utiliza el compilador C para compilar el archivo .h y el archivo .c
que se han creado en los pasos 3 y 4 en una librería compartida. En
terminología Windows, una librería compartida se llama Librería de
Carga Dinámica (DLL).

Paso 6: Ejecutar el Programa


Y finalmente, utiliza java, el intérprete del lenguaje Java, para
ejecutar el programa.
Paso 1. Escribir el código en Java

El siguiente fragmento de código Java define una clase


llamada HelloWorld. Esta clase tiene un segmento de
código estático.

public class HelloWorld


{
public native void displayHelloWorld();

static
{
System.loadLibrary("hello");
}
}
Definir un método nativo
Todos los métodos, tanto métodos Java, como métodos nativos, se deben definir dentro de una
clase Java. Cuando se escribe la implementación de un método en un lenguaje de programación
distinto de Java, se debe incluir la palabra clave native como parte de la definición del método
dentro de la clase java.

La clave native indica al compilador Java que la función es una función en lenguaje nativo. Es
sencillo decir que la implementación del método displayHelloWorld() de la clase HelloWorld
está escrita en otro lenguaje de programación porque la clave native aparece como parte de su
definición de método.

public native void displayHelloWorld();

Esta definición de método en nuestra clase Java sólo proporciona la firma del método para
displayHelloWorld(). No proporciona la implementación para el método. Se deberá
proporcionar la implementación para displayHelloWorld() en un archivo fuente separado en el
lenguaje nativo.

La definición de método para displayHelloWorld() también indica que el método es un método


de ejemplar público, no acepta ningún argumento, y no devuelve ningún valor. Para más
información sobre los argumentos y los valores de retorno desde métodos nativos puedes ver
Programación de Interface Nativo en Java.
Carga de la librería
Se debe compilar el código de lenguaje nativo que implementa displayHelloWorld dentro de una
librería compartida (se hará esto en el Paso 5: Crear una Librería Compartida). También se debe
cargar la librería compartida dentro de la clase Java que la requiere. Esta carga de la librería
compartida dentro de la clase Java mapea la implementación del método nativo a su definición.

Se utiliza el método System.loadLibrary para cargar la librería compartida que se creo cuando
compilamos la implementación del código. Este método se coloca con un inicializador static. El
argumento de System.loadLibrary es el nombre de la librería. El sistema utiliza un estándard,
pero específico de la plataforma, para convertir el nombre de la librería a un nombre de librería
nativo. Por ejemplo, en Solaris convierte el nombre "hello" a libhello.so, mientras que en Win32
convierte el mismo nombre de librería a hello.dll.

El siguiente inicializador static de la clase HelloWorld carga la librería apropiada, llamada hello.
El sistema de ejecución ejecuta un inicializador estático cuando carga la clase.

static
{
System.loadLibrary("hello");
}
Crear el programa principal
También se debe crear un archivo fuente separado con una aplicación Java que instancia la clase
que contiene la definición de método nativo.

Esta aplicación Java también llamará al método nativo. Como es una aplicación, debe tener un
método main. En un archivo fuente separado, que para este ejemplo hemos llamado Main.java, se
crea una aplicación Java que instancia HelloWorld y llama al método nativo
displayHelloWorld().

public class Main


{
public static void main(String[] args)
{
HelloWorld saludo = new HelloWorld();
saludo.displayHelloWorld();
}
}

Como se puede ver a partir del código anterior, se llama a un método nativo de la misma forma en
que se llamaría a un método normal: sólo añadir el nombre del método al final del nombre del
objeto, separado con un punto ('.'). Una pareja de paréntesis, ( ), sigue al nombre del método y
encierra los argumentos. El método displayHelloWorld() no tiene ningún argumento.
Paso 2. Compilar el código Java

Se utiliza el compilador del lenguaje Java para compilar la clase Java


creada en el paso anterior.

En este momento también se debería compilar la aplicación Java que se


escribió para probar el método nativo.
Paso 3. Crear el archivo .h
En este paso se utiliza el programa de utilidad javah para generar un
archivo de cabecera (un archivo .h) desde la clase Java HelloWorld.

El archivo de cabecera proporciona un prototipo de función para la


implementación del método nativo displayHelloWorld() definido en esta
clase.

Ejecutamos javah ahora sobre la clase HelloWorld que se creó en los pasos
anteriores.

Por defecto, javah sitúa el nuevo archivo .h en el mismo directorio que el


archivo .class. Se utiliza la opción -d para instruir a javah para que sitúe el
archivo de cabecera en un directorio diferente.

El nombre del archivo de cabecera es el nombre de la clase java con


extensión .h. Por ejemplo, el comando anterior generará un archivo llamado
HelloWorld.h.
La definición de función
Veamos el archivo de cabecera HelloWorld.h.

JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld (JNIEnv *,


jobject );

Java_HelloWorld_displayHelloWorld() es la función que proporciona la


implementación del método nativo de la clase HelloWorld, que se escribirá en el paso
4. Se utiliza la firma de la función cuando se escribe la implementación del método
nativo. Si HelloWorld contuviera otros métodos nativos, sus firmas de función
deberían aparecer aquí también.

El nombre de la función en el lenguaje nativo que implementa el método nativo


consiste en el prefijo Java_, el nombre del paquete, el nombre de la clase, y el
nombre del método nativo. Entre cada nombre de componente hay un subrayado"_"
como separador. El nombre de paquete se omite cuando el método está en el paquete
por defecto.

Así, el método nativo displayHelloWorld dentro de la clase HelloWorld se convierte


en Java_HelloWorld_displayHelloWorld(). En nuestro ejemplo, no hay nombre de
La definición de función
Observe que la implementación de la función en el lenguaje nativo, que
aparece en el archivo de cabecera, acepta dos parámetros, aunque en su
definición en lenguaje Java no aceptará ninguno.

El JNI requiere que cualquier método nativo tenga estos dos parámetros. El
primer parámetro es un puntero a una interfaz JNIEnv. A través de este
puntero, el código nativo podrá acceder a los parámetros y objetos de la
aplicación Java.

El parámetro jobject es una referencia al propio objeto. Para un método


nativo no-estático como el método displayHelloWorld de nuestro ejemplo,
este argumento es una referencia al objeto. Para métodos nativos estáticos,
este argumento sería una referencia al método Java.

Para aclararlo un poco, se puede pensar en el parámetro jobject como en la


variable "this" de C++. Nuestro ejemplo ignora ambos parámetros.
Paso 4. Escribir la implementación
del método nativo
Ahora podemos entrar en el negocio de escribir la implementación del método nativo en otro lenguaje distinto de
java.

La función que escribamos debe tener la misma firma de función que la que se generó con javah dentro del
fichero HelloWorld.h en el paso 3. Recordemos que la firma de la función generada para el método nativo
displayHelloWorld() de la clase HelloWolrd, se parece a esto.

JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld(JNIEnv *, jobject);

Aquí esta la implementación en lenguaje C para el método nativo Java_HelloWorld_displayHelloWorld(). Esta


implementación se encuentra en el archivo llamado HelloWorldImp.c.

#include <jni.h>
#include "HelloWorld.h"
#include <stdio.h>

JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld(JNIEnv *env, jobject obj)


{
printf("Hello world!\n");
return;
}
Paso 4. Escribir la implementación
del método nativo

La implementación para Java_HelloWorld_displayHelloWorld() es


correcta: la función utiliza la función printf() para mostrar el string
"Hello World!" y retorna.

Este archivo incluye tres archivos de cabecera.

jni.h - Este archivo de cabecera proporciona información que el código


nativo necesita para interactuar con el sistema de ejecución Java.
Cuando se escriban métodos nativos, siempre se debe incluir este
archivo de cabecera en los archivos fuente nativos.

HelloWorld.h - El archivo .h que se generó en el paso 3.

stdio.h - El código anterior también incluye el archivo stdio.h porque


utiliza la función printf().
Paso 5. Crear una librería
compartida
En el paso anterior, creamos un archivo C en el que escribimos la
implementación para el método nativo displayHelloWorld. Se
grabó el método nativo en el archivo HelloWorldImp.c. Ahora,
deberemos compilar este archivo en una librería, que debe llamarse
hello para corresponder con el nombre utilizado en el método
System.loadLibrary.

En Win32, el siguiente comando construye una librería de enlace


dinámico hello.dll utilizando Microsoft Visual C++.

Cl –I“C:\Program Files\Java\jdk1.8.0_60\include” –I“C:\Program Files\


Java\jdk1.8.0_60\include\win32” –LD HelloWorldImp.c -Fehello.dll

Por supuesto, se necesita especificar el path de include que


corresponda con la configuración de nuestra máquina.
Paso 6. Ejecutar el programa
Ahora ejecuta la aplicación Java (La clase Main) con el intérprete java. Deberías ver
la siguiente salida.

Hello World!

Si ves una excepción como ésta.

java.lang.UnsatisfiedLinkError: no hello in shared library path


at java.lang.Runtime.loadLibrary(Runtime.java)
at java.lang.System.loadLibrary(System.java)
at
at java.lang.Thread.init(Thread.java)

es porque no tienes configurado correctamente el path de librerías. El path de librerías


es una lista de directorios en la que el sistema de ejecución Java busca cuando se
cargan librerías. Configura el path de librerías, y asegurate del nombre del directorio
donde está la librería hello.

You might also like