2. DLL (Dynamic Link Library) -...

18
8 Software de desarrollo de simulaciones para las pruebas funcionales del avión A400M 2. DLL (Dynamic Link Library) 2.1. ¿Qué es una DLL? Funcionalidad Una biblioteca de vínculos o enlaces dinámicos (Dynamic Link Library, en inglés) no es más que una porción de código ejecutable que puede cargar y usar otro programa bajo demanda. Y no sólo un programa, esta librería puede ser compartida por varios programas. Este nombre se utiliza en exclusiva en sistemas operativos Microsoft Windows aunque este concepto de librería dinámica está presente en todos los sistemas operativos. En el caso de los sistemas operativos de Microsoft en general estas librerías están contenidas en archivos de extensión .dll o incluso dentro archivos ejecutables con extensión .exe. En otros sistemas de tipo Unix por ejemplo suelen tener la extensión .so (shared object). 2.2. Ventajas e inconvenientes. Alternativas A medida que se va escribiendo el código de un programa es fácil darse cuenta de que hay partes del código que se repiten. Si sólo se van a usar para pequeños programas basta con introducir ese código en funciones o procedimientos de otros tipos. Pero para grandes programas con muchos archivos de código, lo ideal sería tener ese código en un lugar separado desde el cual otros códigos puedan acceder a él. Además, si este código es siempre el mismo lo idóneo es que quede compilado de forma definitiva y así el compilador no tendrá que malgastar tiempo de proceso en volver a realizar un trabajo ya hecho y que ya sabemos que está bien. Estos son los conceptos claves de una librería. Una ventaja adicional a esto que se ha comentado de las librerías (reutilización de código y mayor velocidad de compilación y mantenimiento) es que al ser usado por varios programas se trata de un código muy probado, con lo que la eficiencia es mucho mayor. Existen dos tipos de librerías: Librerías estáticas: La porción de código de esta librería se introduce dentro del código del programa, con lo que para usarla un programa tiene que compilarse junto con ella y una vez hecho la librería ya no se usa a la hora de la ejecución. Librerías dinámicas: El código de la librería no se copia al ejecutable una vez compilado, éste permanece ya compilado en un archivo fijo al cual el ejecutable accede en busca de su código. Por tanto a la hora de ejecutar el programa es necesario tanto el ejecutable como la librería, en caso de que ésta no esté habría un error. Una librería de enlace dinámico es por tanto una evolución de las librerías estáticas. La cuestión es, ¿en qué casos es mejor una u otra? ¿Qué otras ventajas e inconvenientes tiene el uso de estas librerías frente a no usarlas además de todo lo anteriormente visto? Se analizará punto por punto según varios aspectos:

Transcript of 2. DLL (Dynamic Link Library) -...

Page 1: 2. DLL (Dynamic Link Library) - bibing.us.esbibing.us.es/proyectos/abreproy/11868/fichero/Proyecto%2F2-DLL.pdf · Una librería de enlace dinámico es por tanto una evolución de

8 Software de desarrollo de simulaciones para las pruebas funcionales del avión A400M

2. DLL (Dynamic Link Library)

2.1. ¿Qué es una DLL? Funcionalidad Una biblioteca de vínculos o enlaces dinámicos (Dynamic Link Library, en inglés) no es más que

una porción de código ejecutable que puede cargar y usar otro programa bajo demanda. Y no

sólo un programa, esta librería puede ser compartida por varios programas. Este nombre se

utiliza en exclusiva en sistemas operativos Microsoft Windows aunque este concepto de

librería dinámica está presente en todos los sistemas operativos. En el caso de los sistemas

operativos de Microsoft en general estas librerías están contenidas en archivos de extensión

.dll o incluso dentro archivos ejecutables con extensión .exe. En otros sistemas de tipo Unix

por ejemplo suelen tener la extensión .so (shared object).

2.2. Ventajas e inconvenientes. Alternativas A medida que se va escribiendo el código de un programa es fácil darse cuenta de que hay

partes del código que se repiten. Si sólo se van a usar para pequeños programas basta con

introducir ese código en funciones o procedimientos de otros tipos. Pero para grandes

programas con muchos archivos de código, lo ideal sería tener ese código en un lugar separado

desde el cual otros códigos puedan acceder a él. Además, si este código es siempre el mismo lo

idóneo es que quede compilado de forma definitiva y así el compilador no tendrá que

malgastar tiempo de proceso en volver a realizar un trabajo ya hecho y que ya sabemos que

está bien. Estos son los conceptos claves de una librería.

Una ventaja adicional a esto que se ha comentado de las librerías (reutilización de código y

mayor velocidad de compilación y mantenimiento) es que al ser usado por varios programas se

trata de un código muy probado, con lo que la eficiencia es mucho mayor.

Existen dos tipos de librerías:

Librerías estáticas: La porción de código de esta librería se introduce dentro del

código del programa, con lo que para usarla un programa tiene que compilarse

junto con ella y una vez hecho la librería ya no se usa a la hora de la ejecución.

Librerías dinámicas: El código de la librería no se copia al ejecutable una vez

compilado, éste permanece ya compilado en un archivo fijo al cual el ejecutable

accede en busca de su código. Por tanto a la hora de ejecutar el programa es

necesario tanto el ejecutable como la librería, en caso de que ésta no esté habría

un error.

Una librería de enlace dinámico es por tanto una evolución de las librerías estáticas. La

cuestión es, ¿en qué casos es mejor una u otra? ¿Qué otras ventajas e inconvenientes tiene el

uso de estas librerías frente a no usarlas además de todo lo anteriormente visto? Se analizará

punto por punto según varios aspectos:

Page 2: 2. DLL (Dynamic Link Library) - bibing.us.esbibing.us.es/proyectos/abreproy/11868/fichero/Proyecto%2F2-DLL.pdf · Una librería de enlace dinámico es por tanto una evolución de

9 Software de desarrollo de simulaciones para las pruebas funcionales del avión A400M

Tamaño de archivo y memoria: Si se usa una librería estática o si no se usan

librerías, al tener que copiarse el código de la librería al ejecutable el tamaño de

éste es mayor. Si ese código es encima compartido por varios programas, al

meterse todo ese código en una librería dinámica el ahorro en espacio en disco

puede ser grande. Al no tener que cargar varias porciones de código iguales sino

sólo una desde la que acceder los programas de forma compartida, ahorra

también en memoria. Sin embargo, siendo realistas, esto suponía una gran ventaja

en otros tiempos en los que el espacio en disco (y sobre todo el espacio en

memoria) era crítico. A día de hoy, en los que los precios y los tamaños de las

memorias de almacenamiento y ejecución han caído y subido respectivamente de

forma significativa esto parece ya una cuestión menor.

Velocidad de ejecución: El uso de librerías estáticas a priori debe proporcionar una

mayor velocidad de ejecución al programa en cuestión, ya que no tiene que “salir

afuera” a buscar otro código con el que continuar la ejecución. La realidad es que

la diferencia de rendimiento no es muy grande considerando la gran velocidad de

procesamiento que tienen los microprocesadores de hoy en día.

Gestión de código y flexibilidad ante cambios: El hecho de que una librería

dinámica tenga una porción de código externo al programa hace que la corrección

de pequeños errores de ese código sea más fácil. Bastaría con cambiar la librería y

todos los programas que la usan continuaría funcionando y con la mejora en el

código implementada.

Origen de las librerías dinámicas: Una librería dinámica puede estar hecha por

otros desarrolladores diferentes a los de los programas que lo usan. Es más,

pueden estar hechas con entornos de desarrollo diferentes las librerías y los

programas. Es lo que ocurre con las de los sistemas operativos, los programas

preparados para éstos utilizan aquellas librerías para acelerar la programación de

aplicaciones. Esto proporciona una ventaja enorme a los programadores, tanto

para realizar aplicaciones que empleen dichas librerías como justo al contrario:

programar librerías que amplíen la funcionalidad de algunos programas. Esto

último será fundamental para el caso posterior que nos ocupará de la

programación de simulaciones usando librerías.

Dependencia de las librerías: El hecho de que varias aplicaciones empleen la

misma librería hace la fiabilidad de ellas dependa de más factores. Eso implica que

si una librería tiene un problema (por ejemplo, porque se borre o porque se

sustituya por una versión anterior inintencionadamente), muchos programas

pueden dejar de funcionar adecuadamente o sencillamente ni siquiera funcionar.

Es un problema conocido de versiones del sistema operativo Microsoft Windows,

en lo que se conoce como “DLL Hell”: era relativamente frecuente que al

desinstalar un programa se desinstalasen sus DLLs que otros programas usaban.

Afortunadamente, en versiones más modernas de este sistema este problema está

más controlado.

Parece claro que hoy en día es fundamental el uso de las DLLs en sistemas Windows para

aprovechar todas sus ventajas. Para códigos relativamente grandes, también parece útil el que

creemos nuestras librerías DLL propias. Y en el ejemplo que se verá con simulaciones, como se

Page 3: 2. DLL (Dynamic Link Library) - bibing.us.esbibing.us.es/proyectos/abreproy/11868/fichero/Proyecto%2F2-DLL.pdf · Una librería de enlace dinámico es por tanto una evolución de

10 Software de desarrollo de simulaciones para las pruebas funcionales del avión A400M

ha dicho, será la forma en que se pueda ampliar la funcionalidad de otros programas. Más

adelante se verá en profundidad.

2.3. Lenguajes de programación, entornos y DLLs Una DLL puede ser escrita en cualquier lenguaje de programación, como C, C++, Pascal, Visual

Basic, etc. De la misma forma, cualquier entorno que use estos lenguajes será capaz de

generar archivos DLL compilados. Algunos de ellos contienen asistentes que pueden facilitar la

tarea de crear el código de una nueva librería, cuestión que veremos más adelante con el

entorno Borland C++ Builder. En cualquier caso, se use el entorno y lenguaje que se use, las

ideas básicas son comunes a todos ellos, así como la estructura, la compartición de datos en

memoria, la carga de funciones, la forma en que se programan…

Por otro lado, cada sistema operativo tendrá sus particularidades en este tema, aunque en

general también la idea es la misma, sólo que quizás algunas funciones estándar puedan ser

diferentes y otros pequeños puntos. También por otro lado, existen librerías de programación

(tales como las MFC de Microsoft) que permiten hacer el mismo trabajo sobre un lenguaje y

también habría que conocer las particularidades de esas librerías.

Es por ello que comenzaremos hablando de esta forma de programación de la forma más

genérica posible para a continuación hacer especial incapié en cómo se haría en el lenguaje

que se usará en los ejemplos posteriores: C y C++, a los entornos que se usarán y también al

sistema operativo al que irá destinado el trabajo: toda la serie de Windows basada en NT (con

el consiguiente API de Win32).

2.4. Aspectos generales de programación de DLLs y de carga de

recursos de una DLL

2.4.1. Puntos clave en la programación de una DLL

2.4.1.1. Objetos exportables

A la hora de escribir el código fuente del que luego se compilará para formar la DLL, los objetos

(funciones y clases) que deban ser accesibles desde otros ejecutables, se denominan

exportables, también callbacks si son funciones, en atención a una denominación muy usual en

la literatura inglesa ("callback functions"). Esta circunstancia debe ser conocida por el

compilador, por lo que es necesario especificar qué recursos se declaran "exportables";

además debe indicarse al "linker" que genere una librería dinámica en vez de un ejecutable

normal, aunque como siempre todo esto depende del entorno que usemos.

Las funciones, clases o tipos de datos de las que constará la DLL se programan en un principio

de la misma manera que como si se hicieran dentro del código del programa que invocará

dicho objeto, aunque hay algunas particularidades. En el caso de sistemas Windows, hay que

usar los especificadores _export y dllexport, como veremos más adelante.

Page 4: 2. DLL (Dynamic Link Library) - bibing.us.esbibing.us.es/proyectos/abreproy/11868/fichero/Proyecto%2F2-DLL.pdf · Una librería de enlace dinámico es por tanto una evolución de

11 Software de desarrollo de simulaciones para las pruebas funcionales del avión A400M

2.4.1.2. Archivo de definición y librería o tabla de importación

Además de las fuentes de la librería, en determinados casos, la creación de una DLL exige la

existencia de dos ficheros auxiliares: una librería de importación y un fichero de definición .def

("definition file"). La primera es una librería estática clásica (.lib o .a) que sirve como índice o

diccionario de la dinámica. El segundo es un fichero ASCII. En caso de ser necesarios, la

creación de estos ficheros auxiliares se realiza generalmente en el mismo momento en que se

crea la librería. Sin embargo, en determinadas circunstancias, especialmente cuando se

dispone de una DLL construida de la que no se tienen las fuentes, la creación exige de

herramientas auxiliares.

La necesidad de tales ficheros depende del compilador y de las circunstancias. La

documentación de Microsoft señala que generalmente, la librería de importación es necesaria

para usar la librería con enlazado estático, pero no para enlazado dinámico (explícito). En

cambio, la documentación de MinGW señala: "la librería de importación es necesaria si (y solo

si) la DLL debe ser utilizada por un compilador distinto de la colección de herramientas

MinGW, ya que estas son perfectamente capaces de enlazar con sus DLLs sin necesidad de

ningún recurso auxiliar".

Sea cual sea la forma utilizada, los recursos declarados exportables son incluidos por el

enlazador en una tabla especial contenida en la DLL, que se llama tabla de exportación

("export table") o tabla de entrada ("entry table"). La tabla de exportación tiene dos tipos de

datos importantes (en realidad son dos tablas): los nombres con que aparecen los recursos y

un número de orden. Cuando una aplicación (.exe o librería dinámica) invoca una función

situada en una librería, el módulo que realiza la invocación puede referirse al recurso por

nombre o por número de orden. Como se puede intuir, la segunda forma es ligeramente más

rápida, ya que no se necesitan comparaciones de cadenas para localizar la entrada, pero en el

caso de Windows Microsoft recomienda que las librerías dinámicas se exporten por nombre;

de lo contrario no se garantiza que nuestras librerías sean utilizables por todas las plataformas

y versiones de Windows.

Cuando un recurso es exportado por número, la parte de nombres de la tabla no necesita ser

residente en la memoria del ejecutable que la utilizará. En cambio, si es exportada por

nombres, dicha tabla sí necesita ser residente, y será cargada en memoria cada vez que el

módulo sea cargado.

Es importante tener en cuenta, sobre todo si se va a construir DLLs que serán utilizadas por

terceros, que los nombres de los recursos exportados no pueden interferir con ningún otro

nombre utilizado por el programa o por otra DLL del sistema, de forma que debemos

asegurarnos que estos nombres serán únicos.

2.4.1.3. Función DLLMain

En la programación de aplicaciones, se puede especificar una función que es el punto de

entrada de la ejecución del programa, normalmente llamada función main y en el caso de la

programación bajo el API de Win32 función WinMain. De la misma manera, las DLLs pueden

tener una función que se considera su punto de entrada y sirve para inicializar variables,

objetos u otros elementos (hilos, procesos, etc) en el momento en el que son cargadas por

parte de otro programa. Es algo totalmente opcional, no es estrictamente necesario tener esta

Page 5: 2. DLL (Dynamic Link Library) - bibing.us.esbibing.us.es/proyectos/abreproy/11868/fichero/Proyecto%2F2-DLL.pdf · Una librería de enlace dinámico es por tanto una evolución de

12 Software de desarrollo de simulaciones para las pruebas funcionales del avión A400M

función dentro del código. En el momento en que se cargue la librería, esta función será

invocada inmediatamente, sin que el programador tenga que hacer ninguna acción adicional

tras la carga de la librería.

Dependiendo del entorno de programación y el lenguaje elegido, puede tener diferentes

nombres, como veremos más adelante en el caso de Borland C++ Builder. Incluso en algunos

compiladores se puede especificar el nombre de dicha función por la línea de comandos. En

general, el prototipo de esta función es, para el caso de la programación en C/C++ y el API de

Win32:

BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)

2.4.1.4. Modificador _export o __export

Como se ha mencionado anteriormente, tanto si se usa el copilador C++ Borland como otros,

los recursos "exportables" pueden ser declarados con los especificadores _export o __export

(son equivalentes). Hay que recordar que C++ dispone de una palabra clave específica: export,

cuyo significado se asemeja al que se utiliza aquí: indicar al compilador que la declaración será

accesible desde otras unidades de compilación. Sin embargo, tener en cuenta que _export y

export no tienen nada que ver entre sí. La primera es una particularidad de ciertos

compiladores; la segunda es una palabra clave del C++ Estándar. De ahí que inicialmente

contenga un “_” o uno doble para especificarlo.

valor-devuelto __export nombre-funcion (argumentos);

valor-devuelto _export nombre-funcion (argumentos);

class _export nombre-de-clase;

tipo-de-dato _export nombre-de-variable;

Los recursos exportables también pueden ser declarados mediante el especificador

__declspec(dllexport):

__declspec(dllexport) valor-devuelto funcion (argumentos);

class __declspec(dllexport) nombre-de-clase;

__declspec(dllexport) tipo-de-dato nombre-de-variable;

Ejemplos:

extern "C" _export double SumaValores(double, double);

class _export claseDeEjemplo;

double _export temperaturaFuel;

extern "C" __declspec(dllexport) double SumaValores(double, double);

class __declspec(dllexport) miNuevaClase { /* ... */ };

Page 6: 2. DLL (Dynamic Link Library) - bibing.us.esbibing.us.es/proyectos/abreproy/11868/fichero/Proyecto%2F2-DLL.pdf · Una librería de enlace dinámico es por tanto una evolución de

13 Software de desarrollo de simulaciones para las pruebas funcionales del avión A400M

__declspec(dllexport) int numPagina;

2.4.2. Puntos clave en la carga de recursos de una DLL

2.4.2.1. Vinculación implícita y explícita

Existen dos formas de vincular una librería DLL a un ejecutable: vinculación implícita y

vinculación explícita.

Para una vinculación implícita a un archivo DLL, los archivos ejecutables, procesos u otras

librerías que pretendan utilizar recursos de una DLL deberán obtener lo siguiente del

proveedor del archivo DLL:

Un archivo de encabezado (archivo .h) que contenga las declaraciones de las funciones

exportadas, clases u otro tipo de objeto exportado. Todas las clases, todas las

funciones y todos los datos deberían tener __declspec(dllimport). El uso de este

especificador es el mismo al que vimos antes con dllexport, un ejemplo podría ser:

__declspec( dllimport ) int i; __declspec( dllimport ) void func();

Una biblioteca de importación (archivos de librería estática .lib) a la que vincularse. El

vinculador crea la biblioteca de importación cuando se genera el archivo DLL.

El archivo de librería dinámica con la extensión .dll.

Los archivos ejecutables que utilizan el archivo DLL deben incluir el archivo de encabezado que

contiene las funciones o clases exportadas en cada archivo de código fuente que contenga

llamadas a esas funciones exportadas. Desde el punto de vista de la programación, las

llamadas a las funciones exportadas son como cualquier otra llamada a función en una

programación sin librerías.

Para generar el archivo ejecutable de llamada deberá vincularlo a la biblioteca de importación.

El sistema operativo deberá ser capaz de encontrar el archivo DLL cuando cargue el archivo

ejecutable de llamada.

Usando este método de vinculación, los recursos exportables de la librería están siempre

disponibles para su uso y la inicialización de esos elementos se produce antes del main del

ejecutable principal. El programador no tendrá que hacer nada especial para usar esos

recursos ni para inicializarlos.

En cuanto a la vinculación explícita, las aplicaciones deben realizar una llamada a función para

cargar explícitamente el archivo DLL en tiempo de ejecución. Para una vinculación explícita a

un archivo DLL, una aplicación debe:

Llamar a LoadLibrary para cargar el archivo DLL y obtener un identificador de módulo.

Llamar a GetProcAddress para obtener un puntero a función para cada función

exportada a la que la aplicación desee llamar. Como las aplicaciones llaman a las

Page 7: 2. DLL (Dynamic Link Library) - bibing.us.esbibing.us.es/proyectos/abreproy/11868/fichero/Proyecto%2F2-DLL.pdf · Una librería de enlace dinámico es por tanto una evolución de

14 Software de desarrollo de simulaciones para las pruebas funcionales del avión A400M

funciones del archivo DLL mediante un puntero, el compilador no genera referencias

externas, por lo que no hay necesidad de vincularse a una biblioteca de importación.

Llamar a FreeLibrary cuando se haya acabado de utilizar el archivo DLL.

A diferencia del método de vinculación anterior, los recursos exportables de la DLL estarán

disponibles sólo en el momento en que se necesiten. La librería es inicializada cuando el

programador decide cargarla y liberada también en el momento en que desee hacerlo (lo cual

puede implicar un menor consumo de memoria y en general de recursos de sistema, además

de un mayor control sobre la librería, aunque quizás una cierta mayor complejidad en cuanto a

la programación).

Como se verá más adelante, en el desarrollo de las simulaciones de las pruebas de combustible

se usa la vinculación explícita, debido principalmente a que en ellas se tiene que cargar otras

librerías de terceros de las que se dispone únicamente de archivos .dll. Además, el primer

procedimiento tiene algunas desventajas que ya se han visto también en el estudio de librerías

estáticas, como el consumo de recursos. Se verá por tanto a continuación más en profundidad

este método de vinculación.

2.4.2.2. La función LoadLibrary

Los procesos llaman a LoadLibrary para vincularse explícitamente a un archivo DLL. Si este

procedimiento se ha realizado correctamente, la función asigna el archivo DLL especificado al

espacio de direcciones del proceso que lo llama y devuelve un identificador al archivo DLL que

se puede usar con otras funciones utilizadas en este tipo de vinculación y que se verán más

adelante, como GetProcAddress y FreeLibrary.

LoadLibrary intenta encontrar el archivo DLL indicado por su ruta y nombre de archivo el cual

se pasa como parámetro mediante la misma secuencia de búsqueda que se utiliza para la

vinculación implícita. Si el sistema no encuentra el archivo DLL o la función de punto de

entrada (recuérdese aquí el DLLMain) devuelve el valor FALSE, LoadLibrary devolverá el valor

NULL. Si la llamada a LoadLibrary especifica un módulo de DLL que se asignó a ese espacio de

direcciones, la función sólo devolverá un identificador del archivo DLL e incrementará la

cuenta de referencia del módulo.

Si el archivo DLL tiene una función de punto de entrada, el sistema operativo llamará a la

función en el contexto del subproceso que llamó a LoadLibrary. No se llamará a la función de

punto de entrada si el archivo DLL ya está asociado al proceso a causa de una llamada anterior

a LoadLibrary sin una llamada correspondiente a la función FreeLibrary (en este caso sería una

carga anidada de una misma librería, caso que no se usará).

Por tanto, para el caso particular de la programación en C/C++ y el API de Win32, el prototipo

de esta función sería:

HINSTANCE LoadLibrary(LPCTSTR lpcNombreArchivoDll);

Page 8: 2. DLL (Dynamic Link Library) - bibing.us.esbibing.us.es/proyectos/abreproy/11868/fichero/Proyecto%2F2-DLL.pdf · Una librería de enlace dinámico es por tanto una evolución de

15 Software de desarrollo de simulaciones para las pruebas funcionales del avión A400M

2.4.2.3. La función GetProcAddress

Una vez la DLL ha sido cargada, a partir de ese momento se puede proceder a la carga de sus

recursos. Para obtener la dirección de una función exportada por esa librería, se usa la función

GetProcAddress, que para Win32 y C/C++ tiene el siguiente prototipo:

FARPROC GetProcAddress(HMODULE hModule, LPCSTR lpProcName);

Esta función toma como primer parámetro el identificador de módulo del archivo DLL cargado

(es decir, lo que devuelve la función LoadLibrary) y toma como segundo el nombre de la

función a la que se desea llamar o el ordinal de exportación de la función. Devuelve un puntero

a función. Una vez obtenido, ya se puede usar esa función en el programa.

Como se mencionó en puntos anteriores, en lugar de especificar un nombre se podría usar un

ordinal de exportación. Sólo podrá obtener este ordinal de exportación si el archivo DLL al que

se está vinculando se ha creado con un archivo de definición de módulos (.def) y si los

ordinales figuran en la lista de funciones del archivo .def correspondiente al archivo DLL.

Utilizar un ordinal de exportación en lugar de un nombre de función para llamar a

GetProcAddress resulta un poco más rápido si el archivo DLL tiene muchas funciones

exportadas, puesto que los ordinales exportados sirven como índices en la tabla de

exportación del archivo DLL. Con un ordinal de exportación, GetProcAddress puede encontrar

la función directamente, sin tener que comparar el nombre especificado con los nombres de

función de la tabla de exportación del archivo DLL. No se empleará este método ya que no se

dispone de los .def de las librerías que se cargarán, como veremos más adelante.

Como está llamando a la función DLL mediante un puntero y no hay comprobación de tipos en

tiempo de compilación, debe asegurarse de que los parámetros pasados a la función son

correctos, para no sobrepasar la memoria asignada en la pila y causar así una infracción de

acceso. Hay que consultar los prototipos de las funciones exportadas por la DLL, por lo que es

clara la necesidad de una adecuada documentación sobre qué es lo que una librería puede

proporcionar a sus usuarios y cómo emplearla.

2.4.2.4. La función FreeLibrary

Los procesos que se vinculan explícitamente a un archivo DLL llaman a la función FreeLibrary

cuando el módulo de DLL deja de ser necesario. Esta función reduce el número de referencias

del módulo y, si dicho número es cero, elimina la asignación del espacio de direcciones del

proceso. El prototipo en Win32 y C/C++ es:

FreeLibrary (HINSTANCE)

Donde su único parámetro es el identificador de la DLL obtenido con el valor devuelto por

LoadLibrary. No hay que olvidar nunca liberar la carga de una DLL en el momento en que ya no

se va a necesitar más de sus recursos exportados.

2.4.3. Ejemplo de creación y carga de DLLs A continuación, se mostrará un ejemplo de programación en C/C++ de creación y carga de una

nueva DLL.

Page 9: 2. DLL (Dynamic Link Library) - bibing.us.esbibing.us.es/proyectos/abreproy/11868/fichero/Proyecto%2F2-DLL.pdf · Una librería de enlace dinámico es por tanto una evolución de

16 Software de desarrollo de simulaciones para las pruebas funcionales del avión A400M

En este sencillo ejemplo de creación de DLL, simplemente se exporta una función que muestra

un mensaje en pantalla:

// miLibreria.c

#include <stdio.h>

__declspec(dllexport) void Funcion1 () {

printf(“Función de mi DLL\n”);

}

// miLibreria.h

__declspec(dllexport) void Funcion1 () ;

Tras compilar el ejemplo anterior se obtiene el archivo miLibreria.dll. A continuación se

muestra un ejemplo de carga de la función de la librería anterior:

#include <windows.h>

typedef void (*FUNCION1)(void);

FUNCION1 Funcion1;

HINSTANCE dllHandle;

int main()

{

dllHandle = LoadLibrary("miLibreria.dll");

if(dllHandle != NULL) // Si no ha habido problemas al cargar la librería

{

Funcion1 = (FUNCION1)GetProcAddress(dllHandle, "FuncionInit");

if(Funcion1 != NULL)

{

Funcion1(); // Llamada a la función cargada

}

else

{

// Error al cargar la función

}

FreeLibrary(dllHandle);

}

}

Como se puede ver, la programación de DLLs no entraña una gran dificultad. Eso sí, se ha de

tener cuidado de llevar un buen control del flujo de la ejecución, teniendo en cuenta que en

Page 10: 2. DLL (Dynamic Link Library) - bibing.us.esbibing.us.es/proyectos/abreproy/11868/fichero/Proyecto%2F2-DLL.pdf · Una librería de enlace dinámico es por tanto una evolución de

17 Software de desarrollo de simulaciones para las pruebas funcionales del avión A400M

algún momento podría haber algún problema en la carga de la librería o de una función,

inspeccionando adecuadamente el valor que se va adquiriendo en los punteros.

2.4.4. Convenciones de llamada y DLLs Hay un aspecto a la hora de programar funciones para una DLL que hay que tener muy en

cuenta: la convención de llamadas.

Las convenciones de llamada de una función se diferencian unas de otras por los siguientes

puntos:

La forma que cada una utiliza para la limpieza de la pila (stack).

El orden de paso de parámetros (derecha a izquierda o a la inversa).

El uso o no de mayúsculas y minúsculas, y ciertos prefijos en los identificadores

globales.

Normalmente, en la programación de aplicaciones con un mismo entorno de desarrollo,

lenguaje de programación y sistema operativo, sea cual sea la convención usada no supone un

problema ya que en todo momento se va a usar una misma convención. Sin embargo, en la

programación de DLLs puede ocurrir que la librería esté hecha con un entorno y la aplicación

que la vaya a usar tenga otro, por lo que las convenciones de llamada podrían ser diferentes y

la ejecución podría ser errónea al producirse un incorrecto paso de parámetros y uso de datos,

por lo que esto es algo con lo que hay que tener especial cuidado.

La especificación de la forma que se utilizará en el programa, puede hacerse a nivel global o

solo a nivel particular de algunas funciones específicas. Para indicarlo a nivel global se utiliza

alguno de los comandos específicos del compilador. La forma de indicarlo a nivel particular es

mediante el uso de ciertas palabras reservadas para que sea utilizada una forma específica en

lugar de la que tenga asignada el compilador por defecto. Estas palabras deben indicarse en la

declaración o prototipo, y delante del especificador de invocación de la función. Son las

siguientes: __cdecl (invocación del lenguaje C), __pascal (invocación en Pascal), __fastcall

(invocación registro), __msfastcall (invocación rápida) y __stdcall (invocación estándar). En la

programación Windows, al incluir la cabecera windows.h, estas convenciones de llamada se

utilizan a través de sus propios typedefs. En concreto se utilizan las siguientes equivalencias

Especificador Windows Equivalente Tipo de invocación

CDECL __cdecl Invocación C

WINAPI __stdcall Estándar

CALLBACK __stdcall Estándar

Tabla 1: Equivalencia de especificadores Windows y tipos de convención de llamada

De la misma forma, una característica que distingue a unos compiladores (y lenguajes) de

otros, es el tratamiento dado a los identificadores de los objetos; lo que se conoce como

Page 11: 2. DLL (Dynamic Link Library) - bibing.us.esbibing.us.es/proyectos/abreproy/11868/fichero/Proyecto%2F2-DLL.pdf · Una librería de enlace dinámico es por tanto una evolución de

18 Software de desarrollo de simulaciones para las pruebas funcionales del avión A400M

sistema de codificación de nombres ("name encoding scheme"). De este sistema depende que

durante las fases intermedias de la compilación, los identificadores sean guardados tal como

los escribe el programador o sufran mutaciones más o menos importantes.

Un ejemplo de esto ocurre en el compilador Borland C++. En BC++, cuando está activada la

opción -u (opción por defecto), el compilador guarda todos los identificadores globales en su

grafía original (mayúsculas, minúsculas o mixta), añadiendo automáticamente un guión bajo

“_” delante de cualquier identificador global, ya sea de función o de variable. Para modificar

este comportamiento se puede utilizar la opción -u- como parámetro en la línea de comando

del compilador.

La siguiente tabla resume el efecto de un modificador aplicado a una función. Por cada

modificador, se muestra el orden en que son colocados en la pila los parámetros de la función.

Después se indica si es la función que realiza la invocación ("Caller"), o la función llamada

("Called"), la responsable de sacar los parámetros fuera de la pila. Finalmente, se muestra el

efecto en el nombre de una función global.

Modificador Colocación de

parámetros

Quién quita

los parámetros

Cambio de nombre

(sólo en lenguaje C)

__cdecl Derecha a izq. func. invocante se añade '_' como prefijo

__fastcall Izq. a derecha func. invocada se añade '@' como prefijo

__pascal Izq. a derecha func. invocada se convierte a Mayúsculas

__stdcall Derecha a izq. func. invocada Sin cambio

Tabla 2: Efecto de los diferentes modificadores de convención de llamada

2.5. Entornos de desarrollo y DLLs. Herramientas. Ahora se estudiarán algunos entornos de desarrollo disponibles para la programación de DLLs

para las simulaciones y se analizarán qué particularidades, ventajas y aspectos a tener en

cuenta para realizar estas tareas.

2.5.1. Borland C++ Builder 5

2.5.1.1. Descripción del entorno

Borland C++ Builder es un entorno integrado de desarrollo (eminentemente visual) con el que

se pueden diseñar, compilar y depurar aplicaciones C++ con una mínima escritura manual de

código. Es el usado principalmente dentro del grupo Test Means en el departamento de

Ingeniería de Sistemas de Avión para el desarrollo del sistema CATS. Es un entorno bastante

intuitivo y ya conocido por lo que es ideal para el desarrollo de simulaciones sin preocuparse

de aprender el manejo de nuevos entornos ni adquirir otros que acarreen un coste mayor al

desarrollo.

Page 12: 2. DLL (Dynamic Link Library) - bibing.us.esbibing.us.es/proyectos/abreproy/11868/fichero/Proyecto%2F2-DLL.pdf · Una librería de enlace dinámico es por tanto una evolución de

19 Software de desarrollo de simulaciones para las pruebas funcionales del avión A400M

Como se puede ver en la figura 3, el entorno es muy intuitivo: consta de una ventana principal

(la superior) con todas las opciones al alcance del usuario, como la gestión de los proyectos y

archivos, compilación, ejecución, depuración y el conjunto de componentes visuales para

insertar en los formularios. En la ventana izquierda se pueden editar las propiedades de cada

uno de los componentes insertados y la ventana central es la de edición, depuración y

ejecución de código.

Figura 1: Vista principal del entorno de desarrollo Borland C++ Builder 5

Este entorno emplea un conjunto de clases llamadas VCL (Visual Component Library), que son

colecciones de objetos escritos en el lenguaje Pascal (por herencia de otro entorno de

desarrollo de Borland, el Delphi). Se trata de una serie de recursos pre-construidos de los que

puede echar mano el programador para integrarlos en sus aplicaciones. De esta manera, en

lugar de emplear los métodos clásicos de programación con el API Win32 se emplean éstos

para aumentar la productividad. Eso sí, a costa de tener que aprender a usar este conjunto de

librerías. Como se parte de que se ha usado frecuentemente este entorno, en una gran parte

esto ya no supone un problema.

Este entorno proporciona además asistentes para la creación de diferentes proyectos típicos

frecuentes. Uno de ellos es el de la creación de una DLL, que pasaremos a estudiar a

continuación.

2.5.1.2. El asistente DLL (DLL Wizard). Creación de una DLL.

Para entrar en este asistente, sólo hay que dirigirse al menú File y a continuación pulsar sobre

la opción New…

Page 13: 2. DLL (Dynamic Link Library) - bibing.us.esbibing.us.es/proyectos/abreproy/11868/fichero/Proyecto%2F2-DLL.pdf · Una librería de enlace dinámico es por tanto una evolución de

20 Software de desarrollo de simulaciones para las pruebas funcionales del avión A400M

Figura 2: Creación de nuevos ítems y el asistente DLL de Borland C++ Builder

En la ventana que se muestra (figura 4) nos da a elegir entre una serie de opciones para crear

nuevos proyectos, librerías, archivos, etc. Pulsando sobre DLL Wizard se entra en dicho

asistente (figura 5) donde se nos da a elegir por un lado el lenguaje con el que se programará

(C o C++), si se desea usar las librerías VCL o no y una última opción para que el código

resultante sea acorde a las características del entorno Visual C++ de Microsoft (esta última

opción no se usará).

Eligiendo lenguaje C++ y usando las VCL, se creará un archivo que sirve como “esqueleto” a la

hora de empezar a programar una DLL. Este esqueleto tiene la siguiente forma:

#include <vcl.h> #include <windows.h> #pragma hdrstop //--------------------------------------------------------------------------- // Important note about DLL memory management when your DLL uses the // static version of the RunTime Library: // // If your DLL exports any functions that pass String objects (or structs/ // classes containing nested Strings) as parameter or function results, // you will need to add the library MEMMGR.LIB to both the DLL project and // any other projects that use the DLL. You will also need to use MEMMGR.LIB // if any other projects which use the DLL will be performing new or delete // operations on any non-TObject-derived classes which are exported from the // DLL. Adding MEMMGR.LIB to your project will change the DLL and its calling // EXE's to use the BORLNDMM.DLL as their memory manager. In these cases, // the file BORLNDMM.DLL should be deployed along with your DLL. // // To avoid using BORLNDMM.DLL, pass string information using "char *" or // ShortString parameters. // // If your DLL uses the dynamic version of the RTL, you do not need to // explicitly add MEMMGR.LIB as this will be done implicitly for you //--------------------------------------------------------------------------- #pragma argsused int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved) { return 1; }

Page 14: 2. DLL (Dynamic Link Library) - bibing.us.esbibing.us.es/proyectos/abreproy/11868/fichero/Proyecto%2F2-DLL.pdf · Una librería de enlace dinámico es por tanto una evolución de

21 Software de desarrollo de simulaciones para las pruebas funcionales del avión A400M

Vemos en primer lugar que además de cargar la librería windows.h se carga la correspondiente

para poder usar la librería de clases VCL, llamada vcl.h. Por otro lado, la directa #pragma

hdrstop sirve para las cabeceras precompiladas, que es una técnica de este entorno que

permite compilar ficheros de cabecera (e incluso código fuente) sólo una vez, y reutilizar este

código ya compilado. Esto elimina la necesidad de que el compilador tenga que recompilar los

ficheros de cabecera de cada uno de los ficheros fuente del proyecto. Este hdrstop sirve para

indicarle al compilador que guarde el estado de compilación de este archivo.

A continuación hay una advertencia sobre si se exporta objetos tipo String de VCL, en el que

hay que incluir una librería de Borland para que funcione adecuadamente.

Por último, vemos algo que ya se indicaba en capítulos anteriores. En lugar de usar como

función de punto de entrada de la DLL el DLLMain se usa un nombre diferente en este

entorno: DLLEntryPoint.

Todo lo indicado anteriormente para la programación de DLLs se cumple para este entorno, las

funciones y sus parámetros son los mismos. Eso sí, hay que tener en cuenta algunos detalles:

La convención de llamada por defecto en este entorno es la del C. Por lo tanto, si

queremos pasar a una convención estándar, hay que usar el modificador __stdcall (o

bien, como se ve en el esqueleto, usar el especificador de Windows WINAPI

equivalente a __stdcall).

Hay que tener en cuenta el problema del planchado de nombres o “name mangling”,

que se describirá a continuación.

2.5.1.3. El problema del “name mangling”

El entorno Borland C++ maneja tanto el lenguaje C como el C++. Cuando el compilador C++

traduce un módulo en el que existen funciones, los identificadores originales son

reemplazados por una versión distorsionada que incluye de una forma codificada los tipos de

argumentos utilizados por la función. Esta distorsión o deformación de nombres, conocida

también como decoración o planchado de nombres ("name mangling"), es la que hace posible

la sobrecarga de funciones. Puesto que aunque dos funciones compartan el mismo nombre, si

son distintos los tipos de argumentos, las versiones internas ("planchadas") de tales nombres

son distintas.

Además, la decoración de nombres es un mecanismo C++ de seguridad (de comprobación de

tipos), que ayuda al enlazador a comprobar si las invocaciones a funciones situadas en otros

módulos son correctas (respecto a la idoneidad de los argumentos utilizados).

Hay que tener en cuenta que este planchado de nombres de las funciones no está especificada

por el estándar C++, de forma que es normal que dos compiladores de fabricantes distintos

planchen las funciones de forma diferente (por ejemplo, Visual C++ de Microsoft y Borland C++

Builder no lo hacen igual). El resultado es que, salvo que se tomen precauciones especiales, no

está garantizado que las librerías construidas con un compilador C++ funcionen con otro. Con

esto se perdería una de las ventajas fundamentales que tienen las librerías DLL.

Page 15: 2. DLL (Dynamic Link Library) - bibing.us.esbibing.us.es/proyectos/abreproy/11868/fichero/Proyecto%2F2-DLL.pdf · Una librería de enlace dinámico es por tanto una evolución de

22 Software de desarrollo de simulaciones para las pruebas funcionales del avión A400M

Para evitar el planchado de nombres, hay que utilizar la declaración extern “C” en la

declaración (prototipo) de la función. Se puede indicar para una sola función o bien para un

grupo o bloque de enlazado:

extern "C" void funcion(int); // para una función extern "C" { // para un bloque de enlazado void funcion1(int); void funcion2(int); void funcion3(int); }; extern "C" { // para todas las de un fichero de cabecera #include "milibreria.h" };

Naturalmente lo anterior tiene un coste: la ausencia de posibilidad del mecanismo de

sobrecarga de funciones en el módulo en el que se evita la decoración de nombres.

2.5.1.4. Depuración de DLLs

Una de las dificultades que conlleva la programación viene de la depuración de código. En el

caso de las DLLs, se puede optar por emplear por dos mecanismos diferentes:

Como las DLLs necesita de un código que la invoque, se podría pensar en hacer dos

cosas a la vez: el código de la librería y en el código de un programa que además de

cargarla permita depurarla integrando herramientas para el paso de parámetros,

control de carga, lectura de variables, etc. con lo cual no sería necesario ahondar en las

peculiaridades que tiene cada entorno en el tema de la depuración. Esta opción quizás

parece más inmediata pero si el proyecto es un poco largo puede resultar al final muy

engorroso.

Directamente depurar la DLL usando las herramientas propias del entorno sobre

depuración, bien a través del código directo de la DLL o bien el programa que invoca

una DLL. Si lo que se está programando es una aplicación que invoca una DLL de la que

no poseemos su código la depuración en este caso no será posible (o no al menos de

forma sencilla, apareciendo el código en lenguaje ensamblador).

Borland C++ Builder tiene poderosas herramientas de depuración que además ya se conocían

por experiencia del uso del entorno y que se usaron para la simulación que se verá más

adelante, aunque no se usó para depurar DLLs. Para poder depurar DLLs hay que configurar

varios parámetros dentro del entorno y otros aspectos a tener en cuenta, pero se comprobó

que para el tipo de programas que se iban a crear no era la solución más factible. Más

adelante, cuando se hable sobre las simulaciones de fuel, se comentarán todas las razones,

pero diremos ahora como adelanto que todas las DLLs de las simulaciones tienen una

característica común: todas constan de 3 funciones de igual nombre (con lo que hacer un

programa de carga de DLLs de simulaciones sería común a todas ellas) y los datos obtenidos

con ellas pueden obtenerse y visualizarse rápidamente usando el entorno SEAS.

Page 16: 2. DLL (Dynamic Link Library) - bibing.us.esbibing.us.es/proyectos/abreproy/11868/fichero/Proyecto%2F2-DLL.pdf · Una librería de enlace dinámico es por tanto una evolución de

23 Software de desarrollo de simulaciones para las pruebas funcionales del avión A400M

2.5.2. MinGW MinGW, contracción de Minimalist GNU for Windows, es una implementación de GNU

Compiler Collection (GCC) y GNU Binutils para el desarrollo de aplicaciones nativas del sistema

Windows. Se trata de una rama diferente seguida en el desarrollo de otro entorno, el Cygwin,

del que luego se hablará. Se puede decir que es una versión reducida de éste, ya que hay

conjuntos de librerías no disponibles para darle una mayor simplificación.

MinGW por tanto sólo es un compilador y un conjunto de librerías. No se trata de un entorno

de desarrollo como Builder. La gestión de los archivos de un proyecto se tiene que hacer de

forma manual, al igual que la edición de su código, no hay librerías de clases propias para su

uso…

La principal ventaja que tiene es muy probablemente que todo es más “estándar”. La

programación es más clásica, en el sentido en que se usan funciones y recursos muy conocidos

de los estándares del C, C++ y del API de Win32. Al compilar DLLs, no se produce planchado de

nombres, con lo que produce una librería en teoría con mejor compatibilidad para que

cualquier programa la pudiera cargar.

Este es el motivo por el que se usó al comienzo de la programación de simulaciones. Se

desconocían todas las medidas a tener en cuenta en Builder y, hasta que se consiguió

comprenderlas, el MinGW permitía obtener mientras lo que se buscaba. También consiguió

solucionar algunos problemas de incompatibilidades de algunas aplicaciones que había que

usar en simulaciones presentaban con el entorno de Borland.

Para generar una DLL se compila de una forma estándar de GCC pero usando un especificador,

el –shared, y estableciendo como extensión en el archivo .dll en lugar de .exe:

gcc -c midll.c gcc -shared -o midll.dll midll.o Como punto negativo, todo aquello que aporta Builder que como se ha comentado no aporta

MinGW, especialmente el uso de las librerías VCL y el diseño de la GUI, que en algunos

aspectos acelera enormemente la programación.

2.5.3. Cygwin Como se ha dicho anteriormente, MinGW comenzó su desarrollo basándose en Cygwin.

Cygwin es algo más que un compilador, es una colección de herramientas desarrolladas para

proporcionar un comportamiento similar a los sistemas Unix en Windows. Para ello dispone de

un Shell, el bash, que simula el de los entornos Unix.

Éste es el tercer entorno empleado para la programación de simulaciones. El motivo de pensar

en el uso de Cygwin fue que MinGW tenía algunas limitaciones por ser una versión reducida de

éste. Por ejemplo, algunas librerías, como las de programación basada en hilos y programación

de comunicación por puerto de serie, no estaban disponibles.

Un inconveniente de este entorno frente a MinGW es que es necesario un archivo llamado

Cygwin1.dll para cualquier aplicación programada, incluídas las DLLs. Hay que tener cuidado

con la localización de este archivo en el sistema.

Page 17: 2. DLL (Dynamic Link Library) - bibing.us.esbibing.us.es/proyectos/abreproy/11868/fichero/Proyecto%2F2-DLL.pdf · Una librería de enlace dinámico es por tanto una evolución de

24 Software de desarrollo de simulaciones para las pruebas funcionales del avión A400M

Quitando estos cambios y añadidos, por lo demás es igual a MinGW.

2.5.4. Microsoft Visual C++ 6 Este es otro de los entornos de desarrollo de los que se dispone para poder usarse para el

desarrollo de DLLs. De la misma forma que el Borland C++ tiene sus propias librerías de clases

(VCL), Visual C++ tiene las suyas, llamadas MFC (Microsoft Foundation Classes).

Figura 3: El interfaz gráfico de usuario del entorno de desarrollo Microsoft Visual C++ 6

Estas librerías de clases no se han empleado para ningún desarrollo, por lo que su uso

implicaba necesariamente tener una documentación y un estudio previo, por tanto parecía

más indicado el empleo el entorno de Borland en lugar de éste. Sin embargo, junto con este

entorno se poseía el código de una librería escrita para este entorno para leer el contenido de

archivos Excel al igual que el entorno de Borland, que para algunas aplicaciones como más

adelante se verá resultó de gran utilidad debido a algunas posibles incompatibilidades de

Borland.

El entorno es muy similar al de Borland C++ Builder, incluso en la distribución de los diferentes

elementos en pantalla en el entorno gráfico. Esto y el hecho de que se usará código estándar C

con sus funciones y no su librería de clases (a diferencia de Borland) hará que su uso sea muy

intuitivo y que no sea necesario ahondar demasiado en sus características.

2.5.5. La herramienta Dependency Walker Visto anteriormente el problema conocido como “name mangling” y las diferentes

convenciones de llamadas en DLLs, existen herramientas muy útiles para ver el contenido

interno de DLLs de las que no se dispone de su código y/o de una documentación adecuada.

También para cerciorarse de los resultados tras la compilación de una DLL en especial sobre

este tema del planchado de nombres.

Una de esas herramientas es la llamada Dependency Walker. Es gratuita y escanea no sólo

DLLs sino también todo tipo de módulo de Windows de 32 o 64 bits (exe, dll, ocx, sys, etc.)

Page 18: 2. DLL (Dynamic Link Library) - bibing.us.esbibing.us.es/proyectos/abreproy/11868/fichero/Proyecto%2F2-DLL.pdf · Una librería de enlace dinámico es por tanto una evolución de

25 Software de desarrollo de simulaciones para las pruebas funcionales del avión A400M

Figura 4: La herramienta Dependency Walker

Como se puede ver en la figura 6, una vez cargado un archivo .dll se puede consultar

información variada sobre él: el tipo de exportación (C, C++, …), el ordinal de la función u otro

recurso exportado, el nombre (con el que se puede ver inmediatamente si está planchado o

no), el punto de entrada, las dependencias con los distintos módulos (en el caso de librerías

compiladas con Cygwin aparecería la dependencia necesaria con el archivo cygwin1.dll del que

se hablaba antes), etc.

Haciendo doble clic sobre una de esas dependencias se consulta información en línea sobre

ella en la web de desarrolladores de Microsoft, aunque no va a ser necesario para el tema de

programación de simulaciones. También indicará si hay conflicto dentro de las funciones de la

DLL con algunas de esos módulos.