Librerías DLL (Dynamic Link Library)

Sistemas Operarativos. Windows. Funciones de Programación. Enlaces Dinámicos

  • Enviado por: Jvanreck
  • Idioma: castellano
  • País: España España
  • 4 páginas
publicidad

Librerías de enlace dinámico en Windows

Estudio de la creación y uso de las librerías de enlace dinámico en Windows

Introducción

Una librería de enlace dinámico, en adelante DLL (Dynamic Link Library), es un archivo que contiene funciones y/o recursos (mapas de bits, definiciones de fuentes, etc.) que podrán ser llamados desde cualquier aplicación Windows.

De hecho, se puede considerar que Windows está construído sobre una gran cantidad de DLL's. La mayoria de librerías de enlace dinámico se guardan en archivos que tienen extensión DLL, pero también pueden ser guardados en archivos con extensiones EXE (ejecutable), DRV (controlador de dispositivo) y FON (fuente de Windows).

La diferencia entre las librerías de enlace dinámico con extensión DLL y el resto, es que las primeras se cargan porque el programa que las ha de utilizar lo pide a Windows y las demás, en cambio, se cargan porque están referenciadas en archivos de inicialización de Windows. Estas referencias pueden ser creadas por el propio Windows o por el programa de instalación de alguna aplicación.

Ventajas e inconvenientes del uso de DLL's

Ventajas

Inconvenientes

Una función definida dentro de una DLL está disponible para cualquier aplicación Windows.

Tienen que estar presentes en la carpeta del sistema antes de ser utilizadas

Se reduce el tamaño de las aplicaciones que utilizan la DLL por la reutilización de su código.

El tiempo de acceso a la DLL por parte de la aplicación que la usa es más lento

Mejora en el tiempo de compilación y/o carga de la aplicación (debido al menor tamaño del código)

Ahorro de espacio en disco.

Las DLL's son independientes de la aplicación

Estructura de una DLL de 32 bits

Una DLL se puede dividir, básicamente, en tres partes:

  • Archivo de cabecera: Contendrá todas las declaraciones y/o definiciones (de variables, funciones, etc.) que use la DLL.

  • Punto de entrada y salida a la DLL: Es la función principal de la DLL, y es la que se encarga de cargar la DLL (cuando se vaya a usar) y descargarla de la memoria (cuando se deje de usar). Dicha función se llama DllEntryPoint.

  • Funciones que contiene la DLL: Son las funciones que contiene la DLL y que fueron declaradas por el programador de la misma.

Creación de una DLL de 32 bits

Para crear una DLL de 32 bits se puede usar Visual Basic 5.0, Delphi 2.0 o Visual C++ 5.0 o superiores. Para este ejemplo, usaremos Visual C++ 5.0.

El ejemplo que realizaremos es sencillo y constará en una DLL que sea capaz de realizar las cuatro operaciones básicas con dos números (sumar, restar, multiplicar y dividir).

Para ello, se ejecuta Visual C++ 5.0 y se crea un proyecto nuevo del tipo “Win32 Dynamic-Link Library”, especificando como nombre “DLLMat32”.

Una vez creado el proyecto, se crean los siguientes ficheros dentro del mismo:

  • DLLMat32.c: Contendrá el código fuente de las funciones que contendrá la DLL

  • DLLMat32.def: Contendrá la información que indicará al linkador cómo debe crear la DLL o el ejecutable.

Creación del archivo DLLMat32.c

Una vez creado este archivo, lo primero que debemos hacer es incluir el fichero “Windows.h”, que contiene las declaraciones y/o definiciones de las funciones API (de sistema) de Windows.

Luego, se debe implementar la función DllEntryPoint, cuya sintaxis es:

BOOL WINAPI DllEntryPoint (HINSTANCE hInstDLL, DWORD fwdReason, LPVOID lpReserved); donde hInstDLL será el identificador (en adelante, handle) correspondiente a la DLL a la que se está accediendo, fwdReason indicará si se ha de cargar o descargar la DLL y qué lo solicita (un proecso o un sub-proceso), y finalmente lpReserved contiene ciertos parámetros de inicialización de la DLL.

Como los procesos pueden tener sub-procesos que funcionen independientemente, éstos últimos también pueden necesitar cargar o descargar alguna DLL.

Dependiendo de esto, el parámetro fwdReason puede tener los siguientes valores:

Valor (Constante)

Significado

DLL_PROCESS_ATTACH

Un proceso es el que pide la carga de la DLL

DLL_PROCESS_DETACH

Un proceso es el que pide la descarga de la DLL

DLL_THREAD_ATTACH

Un sub-proceso es el que pide la carga de la DLL

DLL_THREAD_DETACH

Un sub-proceso es el que pide la descarga de la DLL

Cuando acaba de ejecutarse, la función DllEntryPoint retorna un valor de tipo boleano. El valor retornado será cierto (TRUE) si se ha ejecutado correctamente la petición que se la ha hecho, o falso (FALSE) si no.

Se tiene que tener en cuenta que dentro de la función DllEntryPoint se tiene que hacer una discriminación del motivo por el cual se ha ejecutado la función (lo indica el parámetro fwdReason), para actuar en consecuencia.

Después, se tiene que incluir el código fuente de las funciones que se quieren implementar, pero teniendo en cuenta dos detalles:

  • Las funciones deberán ir precedidas por el modificador CALLBACK para que puedan ser llamadas desde fuera del segmento de datos de la DLL.

  • Se tienen que definir los prototipos de las funciones en un archivo de cabecera, para que los programadores de aplicaciones que usen la DLL sepan los nombres de las diferentes funciones que incluye y los parámetros que se le han de pasar a cada función.

Creación de los archivos DLLMat32E.h y DLLMat32D.h

Los archivos DLLMat32E.h y DLLMat32D.h serán los archivos cabecera del archivo DLL32Mat.c, de manera que en ellos se declararán los prototipos de las diferentes funciones que lo componene.

En el primero, declararemos las funciones incluidas en la DLL con la siguiente sintaxis:

Tipo_Retorno CALLBACK Función (Tipo1, Tipo2,...).

En el segundo, repetimos la operación pero con la siguiente sintaxis:

Tipo_Retorno CALLBACK (*Puntero_a_Función) (Tipo1, Tipo2,...).

Estos dos archivos serán necesarias cuando creemos un programa que use la DLL (para una mayor ayuda del programador que desconoce las funciones que forman la DLL), pero no para la creación de la misma.

Creación del archivo DLLMat32.def

Este es el último paso para la creación de la DLL. El contenido de este fichero, informa al linkador de Visual C++ sobre cómo debe crear el archivo DLL. Este fichero tendrá que añadirse al proyecto que estamos creando.

El contenido del archivo deberá ser el siguiente:

LIBRARY DLLMat32

DESCRIPTION “Librería Matemática”

EXPORTS

Sumar32

Restar32

Multiplicar32

Dividir32

Significado de cada cláusula:

Cláusula

Significado

LIBRARY

Indica el nombre de la librería que se está creando. Es recomendable que coincida con el nombre del proyecto.

DESCRIPTION

Contiene una breve descripción del contenido de la librería

EXPORTS

Contiene la lista de las funciones exportables, o sea, la lista de funciones de la DLL que podrán ser llamadas desde el exterior.

Compilación y linkado de la librería

Después de compilar el proyecto, el compilador nos creará el archivo DLLMat32.lib. Cuando se linke el proyecto, el linkador nos habrá creado el archivo DLLMat32.DLL.

Ahora, ya podemos probar nuestra DLL. Para acceder a las funciones de una DLL, hay dos métodos: la llamada estática y la llamada dinámica.

Características de los dos tipos de llamadas:

Llamada estática

Llamada dinámica

Ventajas

Inconvenientes

Ventajas

Inconvenientes

La librería se carga automáticamente cuando se carga el ejecutable

La librería incluye su código en el ejecutable que la utiliza

La librería tiene su código almacenado en un archivo de librería.

Hay que pedir a Windows que cargue la librería cuando tenga que usarse

El enlace se produce en tiempo de compilación

Tiene que incluirse en cada ejecutable que la utilice

No tiene que incluirse en cada ejecutable que la utilice

El enlace se produce en tiempo de ejecución, lo que complica su manejo

Las funciones de la DLL pueden ser llamadas como funciones normales

Mayor tamaño de los ejecutables que la usan

Menor tamaño de los ejecutables que la usan

Las funciones DLL tienen que ser llamadas con procedimientos especiales (punteros)

Mayor repetición del código de las DLL's

Menor repetición de código de las DLL's

La DLL permanece en memoria todo el tiempo que dura la ejecución de la aplicación que la usa

La DLL se pude cargar y descargar de memoria según las necesidades de la aplicación que la usa

Dependencia total entre la aplicación y la DLL

Mayor independencia entre la aplicación y la DLL

La llamada estática a una DLL

La llamada estática emplea el archivo de librería estática que nos creó el compilador (DLLMat32.lib).

En este método, el enlace entre la aplicación y las funciones de la DLL se produce durante el proceso de linkado de la aplicación que va a usar la DLL El linkador, coge los archivos objeto (*.obj), los de librerías (*.lib) y los de recursos compilados (*.res) para crear la aplicación para Windows (*.exe).

Esto tiene como consecuencia que la librería incluye su código dentro del ejecutable. Como la librería forma parte del ejecutable, no hace falta cargarla, se carga automáticamente al ejecutar la aplicación. Además, las funciones de la DLL pueden ser llamadas como las otras funciones del programa.

Para probar la llamada estática, creamos un proyecto nuevo que se llama LlamadaEstDLL. En la primera línea de código el archivo, tendremos que incluir la linea #include “DLLMat32E.h”, para que nos reconozca las funciones que en la DLL se incluyen.

La llamada dinámica a una DLL

La llamada dinámica emplea el archivo de librería dinámica que nos creó el linkador (DLLMat32.dll).

En este método, la DLL no se carga automáticamente en memoria (porque no forma parte del ejecutable), y cuando está cargada, desconocemos en que parte de la memoria del ordenador se encuentra.

Para enlazar una función de la DLL con la aplicación, primero tenemos que dar la orden de cargar la DLL en memoria. El enlace entre la aplicación y las funciones de la DLL se produce en tiempo de ejecución. También se puede descargar la DLL de memoria en tiempo de ejecución, de manera que optimizamos la utilización de memoria del ordenador, descargando las DLL's que el proceso deja de usar para poder cargar las que vaya a necesitar.

Para cargar la DLL en memoria, disponemos de la función LoadLibrary, que está declarada de la siguiente manera:

HINSTANCE LoadLibrary (LPCSTR lpLibFileName); donde lpLibFileName es una cadena que contendrá el nombre de la DLL a cargar.

El valor que devuelve es el handle (identificador) de la DLL que hemos cargado, o bien el valor NULL si no se ha podido cargar.

Para, posteriormente, descargar la DLL, usaremos la siguiente función:

BOOL FreeLibrary (HMODULE hLibModule); donde hLibModule es el handle de la DLL (obtenido como respuesta de la función LoadLibrary.

El valor de retorno de la función será cierto (TRUE) si se ha podido descarga la DLL o falso (FALSE) si no se ha podido cargar.

Para conocer la dirección donde se encuentra la función a la que queremos acceder, tendremos que usar la función GerProcAddress, que tiene la siguiente declaración:

FARPROC GetProcAddress (HMODULE hLibModule, LPCSTR lpProcName); donde hLibModule es el handle de la DLL devuelto por LoadLibrary y lpProcName es el nombre de la función de la que se desea conocer su dirección. La función devuelve la dirección de memoria de la función o bien NULL si no ha podido encontrar la función.

Para guardar la dirección, tenemos que asignar el retorno de la función a un puntero, mediante el cual llamaremos a la función. Por ejemplo, si hemos llamado al puntero que apunta a la función “lpfnSumar32”, la forma de llamar a la función sería:

INumero=(lpfnSumar32)(atoi(Texto1),atoi(Texto2));