La vinculación a un archivo DLL puede ocurrir implícitamente durante la compilación del vínculo o explícitamente durante la ejecución. De cualquier manera, la DLL termina cargada en el espacio de memoria del proceso y todos sus puntos de entrada exportados están disponibles para la aplicación.
Si se usa explícitamente en tiempo de ejecución, usa LoadLibrary()
y GetProcAddress()
para cargar manualmente la DLL y obtener punteros a las funciones que necesita llamar.
Si se vincula implícitamente cuando se crea el programa, los códigos auxiliares para cada exportación de DLL utilizada por el programa se vinculan al programa desde una biblioteca de importación, y esos códigos auxiliares se actualizan a medida que se cargan el EXE y el DLL cuando se inicia el proceso. (Sí, lo he simplificado más que un poco aquí ...)
Esos stubs deben provenir de algún lugar, y en la cadena de herramientas de Microsoft provienen de una forma especial de archivo .LIB llamada biblioteca de importación . El .LIB requerido generalmente se crea al mismo tiempo que la DLL y contiene un código auxiliar para cada función exportada desde la DLL.
Confusamente, una versión estática de la misma biblioteca también se enviaría como un archivo .LIB. No hay una forma trivial de distinguirlos, excepto que las LIB que son bibliotecas de importación para DLL normalmente serán más pequeñas (a menudo mucho más pequeñas) que la LIB estática correspondiente.
Si usa la cadena de herramientas de GCC, por cierto, no necesita bibliotecas de importación que coincidan con sus DLL. La versión del enlazador Gnu portado a Windows comprende las DLL directamente y puede sintetizar la mayoría de los stubs necesarios sobre la marcha.
Actualizar
Si simplemente no puede resistirse a saber dónde están realmente todas las tuercas y tornillos y qué está pasando realmente, siempre hay algo en MSDN para ayudarlo. El artículo de Matt Pietrek Una mirada en profundidad al formato de archivo ejecutable portátil Win32 es una descripción general muy completa del formato del archivo EXE y cómo se carga y ejecuta. Incluso se ha actualizado para cubrir .NET y más desde que apareció originalmente en MSDN Magazine ca. 2002.
Además, puede resultar útil saber cómo saber exactamente qué DLL utiliza un programa. La herramienta para eso es Dependency Walker, también conocido como depende.exe. Se incluye una versión con Visual Studio, pero la última versión está disponible de su autor en http://www.dependencywalker.com/ . Puede identificar todas las DLL que se especificaron en el momento del enlace (carga anticipada y carga retardada) y también puede ejecutar el programa y observar cualquier DLL adicional que cargue en tiempo de ejecución.
Actualización 2
He vuelto a redactar parte del texto anterior para aclararlo al volver a leerlo y para usar los términos del arte de vinculación implícita y explícita para mantener la coherencia con MSDN.
Entonces, tenemos tres formas en que las funciones de la biblioteca pueden estar disponibles para ser utilizadas por un programa. La pregunta de seguimiento obvia es entonces: "¿Cómo elijo cuál camino?"
La vinculación estática es la forma en que se vincula la mayor parte del programa en sí. Todos sus archivos de objeto se enumeran y el vinculador los recopila en el archivo EXE. En el camino, el enlazador se encarga de tareas menores como arreglar referencias a símbolos globales para que sus módulos puedan llamar a las funciones de los demás. Las bibliotecas también se pueden vincular estáticamente. Un bibliotecario recopila los archivos de objeto que componen la biblioteca en un archivo .LIB en el que el vinculador busca módulos que contengan los símbolos necesarios. Un efecto de la vinculación estática es que sólo los módulos de la biblioteca que utiliza el programa están vinculados a ella; otros módulos se ignoran. Por ejemplo, la biblioteca matemática tradicional de C incluye muchas funciones de trigonometría. Pero si lo vincula y usacos()
, no termina con una copia del código para sin()
oa tan()
menos que también llame a esas funciones. Para bibliotecas grandes con un rico conjunto de características, esta inclusión selectiva de módulos es importante. En muchas plataformas, como los sistemas integrados, el tamaño total del código disponible para su uso en la biblioteca puede ser grande en comparación con el espacio disponible para almacenar un ejecutable en el dispositivo. Sin la inclusión selectiva, sería más difícil gestionar los detalles de los programas de creación para esas plataformas.
Sin embargo, tener una copia de la misma biblioteca en cada programa en ejecución crea una carga para un sistema que normalmente ejecuta muchos procesos. Con el tipo correcto de sistema de memoria virtual, las páginas de memoria que tienen un contenido idéntico solo necesitan existir una vez en el sistema, pero pueden ser utilizadas por muchos procesos. Esto crea un beneficio para aumentar las posibilidades de que las páginas que contienen código sean idénticas a alguna página en tantos otros procesos en ejecución como sea posible. Pero, si los programas se vinculan estáticamente a la biblioteca en tiempo de ejecución, entonces cada uno tiene una combinación diferente de funciones, cada una de las cuales se presenta en el mapa de memoria de los procesos en diferentes ubicaciones, y no hay muchas páginas de códigos compartibles a menos que sea un programa que por sí solo es ejecutar en más de proceso. Así que la idea de una DLL obtuvo otra gran ventaja.
Una DLL para una biblioteca contiene todas sus funciones, listas para ser utilizadas por cualquier programa cliente. Si muchos programas cargan esa DLL, todos pueden compartir sus páginas de códigos. Todo el mundo gana. (Bueno, hasta que actualice una DLL con una nueva versión, pero eso no es parte de esta historia. Google DLL Hell para ese lado de la historia).
Por lo tanto, la primera gran elección que debe tomar al planificar un nuevo proyecto es entre la vinculación dinámica y estática. Con el enlace estático, tiene menos archivos para instalar y es inmune a que terceros actualicen una DLL que usa. Sin embargo, su programa es más grande y no es tan buen ciudadano del ecosistema de Windows. Con el enlace dinámico, tiene más archivos para instalar, es posible que tenga problemas con un tercero que actualice una DLL que usa, pero generalmente está siendo más amigable con otros procesos en el sistema.
Una gran ventaja de una DLL es que se puede cargar y utilizar sin volver a compilar o incluso volver a vincular el programa principal. Esto puede permitir que un proveedor de bibliotecas de terceros (piense en Microsoft y el tiempo de ejecución de C, por ejemplo) corrija un error en su biblioteca y la distribuya. Una vez que un usuario final instala la DLL actualizada, inmediatamente obtiene el beneficio de esa corrección de errores en todos los programas que usan esa DLL. (A menos que rompa cosas. Consulte DLL Hell.)
La otra ventaja proviene de la distinción entre carga implícita y explícita. Si realiza un esfuerzo adicional de carga explícita, es posible que la DLL ni siquiera existiera cuando se escribió y publicó el programa. Esto permite mecanismos de extensión que pueden descubrir y cargar complementos, por ejemplo.
lib /list xxx.lib
ylink /dump /linkermember xxx.lib
. Consulte esta pregunta de Stack Overflow .