Qué es una DLL

En este artículo se describe qué es una biblioteca de vínculos dinámicos (DLL) y los distintos problemas que pueden producirse cuando se usa. También describe algunos problemas avanzados que debe tener en cuenta al desarrollar sus propias DLL.

Se aplica a todas las ediciones de Windows 10
Número KB original: 815065

Resumen

Al describir lo que es una DLL, en este artículo se describen los métodos de vinculación dinámica, las dependencias de DLL, los puntos de entrada de DLL, la exportación de funciones de DLL y las herramientas de solución de problemas de DLL.

Este artículo finaliza con una comparación de alto nivel de DLL con los ensamblados de Microsoft .NET Framework.

Para los sistemas operativos Windows, DLL proporciona gran parte de la funcionalidad del sistema operativo. Además, al ejecutar un programa en uno de estos sistemas operativos Windows, gran parte de la funcionalidad del programa pueden proporcionarla DLL. Por ejemplo, algunos programas pueden contener muchos módulos diferentes y cada módulo del programa está contenido y distribuido en DLL.

El uso de DLL ayuda a promover la modularización del código y su reutilización, el uso eficaz de la memoria y la reducción del espacio en disco. Por lo tanto, el sistema operativo y los programas se cargan más rápido, se ejecutan a mayor velocidad y ocupan menos espacio en disco del equipo.

Cuando un programa usa una DLL, un problema denominado dependencia puede hacer que el programa no se ejecute. Cuando un programa usa una DLL, se crea una dependencia. Si otro programa sobrescribe e interrumpe esta dependencia, es posible que el programa original no se ejecute correctamente.

Con la introducción de .NET Framework, la mayoría de los problemas de dependencia se han eliminado mediante ensamblados.

Más información

Una DLL es una biblioteca que contiene código y datos que pueden usar más de un programa al mismo tiempo. Por ejemplo, en sistemas operativos Windows, la DLL Comdlg32 ejecuta funciones comunes relacionadas con los cuadros de diálogo. Cada programa puede usar la funcionalidad contenida en esta DLL para implementar un cuadro de diálogo Abrir. Ayuda a promover la reutilización del código y el uso eficaz de memoria.

Mediante el uso de una DLL, un programa se puede modular en componentes independientes. Por ejemplo, un programa de contabilidad puede venderse por módulos. Cada módulo se puede cargar en el programa principal en tiempo de ejecución, si está instalado. Dado que los módulos son independientes, el tiempo de carga del programa es más rápido. Y un módulo solo se carga cuando se solicita esa funcionalidad.

Además, las actualizaciones son más fáciles de aplicar a cada módulo sin afectar a otras partes del programa. Por ejemplo, puede tener un programa de nóminas y los tipos impositivos cambian cada año. Cuando estos cambios están aislados en una DLL, puede aplicar una actualización sin necesidad de compilar o instalar todo el programa de nuevo.

En la lista siguiente se describen algunos de los archivos que se implementan como DLL en sistemas operativos Windows:

  • Archivos de controles ActiveX (.ocx)

    Un ejemplo de un control ActiveX es un control de calendario que le permite seleccionar una fecha de un calendario.

  • Archivos Panel de control (.cpl)

    Un ejemplo de un archivo .cpl es un elemento que se encuentra en Panel de control. Cada elemento es una DLL especializada.

  • Archivos de controlador de dispositivo (.drv)

    Un ejemplo de controlador de dispositivo es un controlador de impresora que regula la impresión en una impresora.

Ventajas de las DLL

La siguiente lista describe algunas de las ventajas que ofrece que un programa utilice una DLL:

  • Usa menos recursos

    Cuando varios programas usan la misma biblioteca de funciones, una DLL puede reducir la duplicación de código que se carga en el disco y en la memoria física. Puede influir en gran medida en el rendimiento no solo del programa que se ejecuta en primer plano, sino también de otros que se ejecutan en el sistema operativo Windows.

  • Promueve la arquitectura modular

    Una DLL fomenta el desarrollo de programas modulares. Le ayuda a desarrollar programas grandes que requieren varias versiones de lenguaje o un programa que requiere una arquitectura modular. Un ejemplo de un programa modular es un programa de contabilidad que tiene muchos módulos que se pueden cargar de forma dinámica en tiempo de ejecución.

  • Facilita la implementación y la instalación

    Cuando una función dentro de una DLL necesita una actualización o una corrección, la implementación e instalación de la DLL no requiere que el programa se vuelva a vincular con ella. Además, si varios programas usan la misma DLL, todos se beneficiarán de la actualización o la corrección. Este problema puede producirse con más frecuencia cuando se usa una DLL de terceros que se actualiza o se corrige de forma regular.

Dependencias de las DLL

Cuando un programa o una DLL usa una función de DLL en otra DLL, se crea una dependencia. El programa ya no es independiente y puede sufrir problemas si se interrumpe la dependencia. Por ejemplo, es posible que no se ejecute si se produce una de las siguientes acciones:

  • Una DLL dependiente se actualiza a una nueva versión.
  • Una DLL dependiente se corrige.
  • Una DLL dependiente se sobrescribe con una versión anterior.
  • Una DLL dependiente se quita del equipo.

Estas acciones se conocen como conflictos de DLL. Si no se aplica la compatibilidad con versiones anteriores, es posible que el programa no se ejecute correctamente.

En la siguiente lista se describen los cambios que se han introducido en Windows 2000 y en posteriores sistemas operativos Windows para ayudar a minimizar los problemas de dependencia:

  • Protección de archivos de Windows

    En Protección de archivos de Windows, el sistema operativo impide que un agente no autorizado actualice o elimine las DLL del sistema. Cuando una instalación de programa intenta quitar o actualizar una DLL que se define como una DLL del sistema, Protección de archivos de Windows buscará una firma digital válida.

  • DLL privadas

    Las DLL privadas permiten aislar un programa de los cambios ejecutados en DLL compartidas. Las DLL privadas usan información específica de la versión o un archivo .local vacío para aplicar la versión de la DLL que usa el programa. Para usar DLL privadas, búsquelas en la carpeta raíz del programa. A continuación, para los nuevos programas, añada información específica de la versión a la DLL. Para programas antiguos, use un archivo .local vacío. Cada método indica al sistema operativo que use las DLL privadas que se encuentran en la carpeta raíz del programa.

Herramientas de solución de problemas de DLL

Hay varias herramientas disponibles para ayudarle a solucionar problemas de DLL. Las herramientas siguientes son algunas de ellas.

Dependency Walker

La herramienta Dependency Walker puede examinar de forma recursiva todas las DLL dependientes que usa un programa. Al abrir un programa en Dependency Walker, este lleva a cabo las siguientes comprobaciones:

  • Dependency Walker comprueba si faltan DLL.
  • Dependency Walker busca archivos de programa o DLL que no son válidos.
  • Dependency Walker comprueba que las funciones de importación y exportación coinciden.
  • Dependency Walker comprueba si hay errores de dependencia circular.
  • Dependency Walker comprueba si hay módulos que no son válidos porque son para un sistema operativo diferente.

Con Dependency Walker, puede documentar todas las DLL que usa un programa. Puede ayudar a prevenir y corregir los problemas de DLL que pueden producirse en el futuro. Dependency Walker se encuentra en el directorio siguiente al instalar Visual Studio 6.0:

drive\Program Files\Microsoft Visual Studio\Common\Tools

DLL Universal Problem Solver

La herramienta DLL Universal Problem Solver (DUPS) se usa para auditar, comparar, documentar y mostrar información de DLL. En la lista siguiente se describen las utilidades que componen la herramienta DUPS:

  • Dlister.exe

    Esta utilidad enumera todas las DLL del equipo y registra la información en un archivo de texto o en un archivo de base de datos.

  • Dcomp.exe

    Esta utilidad compara las DLL que aparecen en dos archivos de texto y genera un tercer archivo de texto que contiene las diferencias.

  • Dtxt2DB.exe

    Esta utilidad carga los archivos de texto creados mediante Dlister.exe y Dcomp.exe en la base de datos dllHell.

  • DlgDtxt2DB.exe

    Esta utilidad proporciona una versión de la interfaz de usuario gráfica (GUI) de la utilidad Dtxt2DB.exe.

Base de datos de ayuda de DLL

La base de datos de ayuda de DLL le ayuda a localizar versiones específicas de DLL instaladas por productos de software de Microsoft.

Desarrollo de DLL

En esta sección se describen los problemas y los requisitos que debe tener en cuenta al desarrollar sus propias DLL.

Tipos de DLL

Al cargar una DLL en una aplicación, dos métodos de vinculación permiten llamar a las funciones de DLL exportadas. Los dos métodos de vinculación son la vinculación dinámica en tiempo de carga y la vinculación dinámica en tiempo de ejecución.

Vinculación dinámica en tiempo de carga

En la vinculación dinámica en tiempo de carga, una aplicación ejecuta llamadas explícitas a funciones de DLL exportadas, como funciones locales. Para usar la vinculación dinámica en tiempo de carga, proporcione un archivo de encabezado (.h) y un archivo de biblioteca de importación (.lib) al compilar y vincular la aplicación. Al hacerlo, el enlazador proporcionará al sistema la información necesaria para cargar la DLL y resolver las ubicaciones de función DLL exportadas en tiempo de carga.

Vinculación dinámica en tiempo de ejecución

En la vinculación dinámica en tiempo de ejecución, una aplicación llama a la función LoadLibrary o LoadLibraryEx para cargar la DLL en tiempo de ejecución. Una vez cargada correctamente la DLL, use la función GetProcAddress para obtener la dirección de la función DLL exportada a la que desea llamar. Cuando se usa la vinculación dinámica en tiempo de ejecución, no se necesita un archivo de biblioteca de importación.

En la lista siguiente se describen los criterios de aplicación para cuándo usar la vinculación dinámica en tiempo de carga y cuándo la vinculación dinámica en tiempo de ejecución:

  • Rendimiento de inicio

    Si el rendimiento de inicio en un primer momento de la aplicación es importante, debe usar la vinculación dinámica en tiempo de ejecución.

  • Facilidad de uso

    En la vinculación dinámica en tiempo de carga, las funciones de DLL exportadas son como las funciones locales. Esto facilita la llamada a estas funciones.

  • Lógica de la aplicación

    En la vinculación dinámica en tiempo de ejecución, una aplicación puede bifurcarse para cargar módulos diferentes según sea necesario. Es importante al desarrollar versiones en varios idiomas.

Punto de entrada de DLL

Al crear una DLL, puede especificar de forma opcional una función de punto de entrada. Se llama a la función de punto de entrada cuando los procesos o subprocesos se unen a la DLL o se separan de ella. Puede usar la función de punto de entrada para inicializar estructuras de datos o para destruirlas según requiera la DLL. Además, si la aplicación es multiproceso, puede usar el Almacén local de subprocesos (TLS) para asignar memoria privada a cada subproceso de la función de punto de entrada. El código siguiente es un ejemplo de la función de punto de entrada de DLL.

BOOL APIENTRY DllMain(
HANDLE hModule,// Handle to DLL module
DWORD ul_reason_for_call,// Reason for calling function
LPVOID lpReserved ) // Reserved
{
    switch ( ul_reason_for_call )
    {
        case DLL_PROCESS_ATTACHED: // A process is loading the DLL.
        break;
        case DLL_THREAD_ATTACHED: // A process is creating a new thread.
        break;
        case DLL_THREAD_DETACH: // A thread exits normally.
        break;
        case DLL_PROCESS_DETACH: // A process unloads the DLL.
        break;
    }
    return TRUE;
}

Cuando la función de punto de entrada devuelve un valor FALSE, la aplicación no se iniciará si usa la vinculación dinámica en tiempo de carga. Si usa la vinculación dinámica en tiempo de ejecución, solo no se cargará la DLL individual.

La función de punto de entrada solo debe efectuar tareas de inicialización sencillas y no debe llamar a ninguna otra función de carga o finalización de DLL. Por ejemplo, en la función de punto de entrada, no debe llamar directa o indirectamente a la función LoadLibrary o LoadLibraryEx. Además, no debe llamar a la función FreeLibrary cuando se termina el proceso.

Nota:

En las aplicaciones multiproceso, asegúrese de que el acceso a los datos globales de DLL está sincronizado (es seguro para subprocesos) para evitar posibles daños en los datos. Para ello, use TLS para proporcionar datos únicos para cada subproceso.

Exportación de funciones de DLL

Para exportar funciones DLL, puede añadir una palabra clave function a las funciones de DLL exportadas o crear un archivo de definición de módulo (.def) que muestre las funciones de DLL exportadas.

Para usar una palabra clave function, debe declarar cada función que desee exportar con la siguiente palabra clave:
__declspec(dllexport)

Para usar funciones de DLL exportadas en la aplicación, debe declarar cada función que desee importar con la palabra clave siguiente: __declspec(dllimport)

Normalmente, se usa un archivo de encabezado que tiene una instrucción define y una ifdef para separar la instrucción export y import.

También puede usar un archivo de definición de módulo para declarar funciones de DLL exportadas. Cuando se usa un archivo de definición de módulo, no es necesario añadir la palabra clave function a las funciones de DLL exportadas. En el archivo de definición de módulo, declara la instrucción LIBRARY y EXPORTS para la DLL. El siguiente código es un ejemplo de archivo de definición.

// SampleDLL.def
//
LIBRARY "sampleDLL"
EXPORTS HelloWorld

DLL y aplicación de muestra

En Visual C++ 6.0, puede crear una DLL seleccionando el tipo de proyecto Win32 Dynamic-Link Library o MFC AppWizard (dll).

El código siguiente es un ejemplo de una DLL que se creó en Visual C++ mediante el tipo de proyecto Win32 Dynamic-Link Library.

// SampleDLL.cpp
//

#include "stdafx.h"
#define EXPORTING_DLL
#include "sampleDLL.h"
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved
)
{
    return TRUE;
}

void HelloWorld()
{
    MessageBox( NULL, TEXT("Hello World"), TEXT("In a DLL"), MB_OK);
}

// File: SampleDLL.h
//
#ifndef INDLL_H
    #define INDLL_H
    #ifdef EXPORTING_DLL
        extern __declspec(dllexport) void HelloWorld();
    #else
        extern __declspec(dllimport) void HelloWorld();
    #endif

#endif

El código siguiente es un ejemplo de un proyecto Win32 Application que llama a la función de DLL exportada en la DLL SampleDLL.

// SampleApp.cpp
//
#include "stdafx.h"
#include "sampleDLL.h"
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    HelloWorld();
    return 0;
}

Nota:

En la vinculación dinámica en tiempo de carga, debe vincular la biblioteca de importación SampleDLL.lib que se crea al compilar el proyecto SampleDLL.

En la vinculación dinámica en tiempo de ejecución, se usa un código similar al siguiente para llamar a la función de DLL exportada de SampleDLL.dll.

...
typedef VOID (*DLLPROC) (LPTSTR);
...
HINSTANCE hinstDLL;
DLLPROC HelloWorld;
BOOL fFreeDLL;

hinstDLL = LoadLibrary("sampleDLL.dll");
if (hinstDLL != NULL)
{
    HelloWorld = (DLLPROC) GetProcAddress(hinstDLL, "HelloWorld");
    if (HelloWorld != NULL)
        (HelloWorld);
    fFreeDLL = FreeLibrary(hinstDLL);
}
...

Al compilar y vincular la aplicación SampleDLL, el sistema operativo Windows busca la DLL SampleDLL en las siguientes ubicaciones, en este orden:

  1. La carpeta de la aplicación

  2. La carpeta actual

  3. La carpeta del sistema Windows

    Nota:

    La función GetSystemDirectory devuelve la ruta de acceso de la carpeta del sistema Windows.

  4. La carpeta Windows

    Nota:

    La función GetWindowsDirectory devuelve la ruta de acceso de la carpeta Windows.

El ensamblado .NET Framework

Con la introducción de .NET y .NET Framework, la mayoría de los problemas asociados a las DLL se han eliminado mediante ensamblados. Un ensamblado es una unidad lógica de funcionalidad que se ejecuta bajo el control de Common Language Runtime (CLR) de .NET. Un ensamblado existe físicamente como un archivo .dll o .exe. Sin embargo, internamente, un ensamblado es diferente de una DLL de Microsoft Win32.

Un archivo de ensamblado contiene un manifiesto de ensamblado, metadatos de tipo, código de lenguaje intermedio de Microsoft (MSIL) y otros recursos. El manifiesto de ensamblado contiene los metadatos del ensamblado que proporcionan toda la información necesaria para que se describa por sí mismo. La siguiente información se incluye en el manifiesto de ensamblado:

  • Nombre de ensamblado
  • Información de versión
  • Información de referencia cultural
  • Información de nombre seguro
  • Lista de archivos del ensamblado
  • Información de referencia de tipo
  • Información de ensamblado dependiente y al que se hace referencia

El código MSIL contenido en el ensamblado no se puede ejecutar directamente. En su lugar, la ejecución de código MSIL se administra a través de CLR. De forma predeterminada, al crear un ensamblado, es privado para la aplicación. Para crear un ensamblado compartido, es necesario asignarle un nombre seguro y, después, publicarlo en la caché global de ensamblados.

En la lista siguiente se describen algunas de las características de los ensamblados en comparación con las de las DLL de Win32:

  • Autodescriptivos

    Al crear un ensamblado, toda la información necesaria para que CLR lo ejecute se incluye en su manifiesto. El manifiesto de ensamblado contiene una lista de los ensamblados dependientes. Por lo tanto, CLR puede mantener un conjunto coherente de ensamblados que se usan en la aplicación. En las DLL de Win32, no se puede mantener la coherencia entre un conjunto de DLL que se utilizan en una aplicación cuando se usan DLL compartidas.

  • Control de versiones

    En un manifiesto de ensamblado, CLR registra y aplica la información de la versión. Además, las directivas de versión permiten aplicar el uso específico de la versión. En las DLL de Win32, el sistema operativo no puede imponer el control de versiones. Debe asegurarse de que las DLL sean compatibles con versiones anteriores.

  • Implementación en paralelo

    Los ensamblados admiten la implementación en paralelo. Una aplicación puede usar una versión de un ensamblado y otra una diferente. A partir de Windows 2000, la implementación en paralelo es compatible con la búsqueda de DLL en la carpeta de la aplicación. Además, Protección de archivos de Windows impide que un agente no autorizado sobrescriba o sustituya las DLL del sistema.

  • Autocontención y aislamiento

    Una aplicación desarrollada mediante un ensamblado puede ser independiente y aislada de otras que se ejecutan en el equipo. Esta característica ayuda a crear instalaciones de impacto cero.

  • Ejecución

    Un ensamblado se ejecuta con los permisos de seguridad proporcionados en el manifiesto de ensamblado y controlados por CLR.

  • Independiente del idioma

    Un ensamblado se puede desarrollar mediante cualquiera de los lenguajes .NET admitidos. Por ejemplo, puede desarrollar un ensamblado en Microsoft Visual C# y, después, usarlo en un proyecto de .NET Visual Basic.

Recolección de datos

Si necesita ayuda del soporte técnico de Microsoft, le recomendamos que recopile la información siguiendo los pasos mencionados en Recopilación de información mediante TSS para problemas relacionados con la implementación.

Referencias