DLL - Guía rápida

La vinculación dinámica es un mecanismo que vincula aplicaciones a bibliotecas en tiempo de ejecución. Las bibliotecas permanecen en sus propios archivos y no se copian en los archivos ejecutables de las aplicaciones. Los archivos DLL se vinculan a una aplicación cuando se ejecuta la aplicación, en lugar de cuando se crea. Las DLL pueden contener enlaces a otras DLL.

Muchas veces, las DLL se colocan en archivos con diferentes extensiones como .EXE, .DRV o .DLL.

Ventajas de DLL

A continuación se presentan algunas ventajas de tener archivos DLL.

Usa menos recursos

Los archivos DLL no se cargan en la RAM junto con el programa principal; no ocupan espacio a menos que sea necesario. Cuando se necesita un archivo DLL, se carga y se ejecuta. Por ejemplo, siempre que un usuario de Microsoft Word esté editando un documento, el archivo DLL de la impresora no es necesario en la RAM. Si el usuario decide imprimir el documento, la aplicación Word hace que se cargue y ejecute el archivo DLL de la impresora.

Promueve la arquitectura modular

Una DLL ayuda a promover el desarrollo de programas modulares. Le ayuda a desarrollar programas grandes que requieren versiones en varios idiomas o un programa que requiere arquitectura modular. Un ejemplo de un programa modular es un programa de contabilidad que tiene muchos módulos que pueden cargarse dinámicamente en tiempo de ejecución.

Ayuda a una fácil implementación e 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 la DLL. Además, si varios programas utilizan la misma DLL, todos se beneficiarán de la actualización o la corrección. Este problema puede ocurrir con más frecuencia cuando utiliza una DLL de terceros que se actualiza o corrige con regularidad.

Las aplicaciones y los archivos DLL se pueden vincular a otros archivos DLL automáticamente, si el vínculo del archivo DLL se especifica en la sección IMPORTACIONES del archivo de definición del módulo como parte de la compilación. De lo contrario, puede cargarlos explícitamente utilizando la función LoadLibrary de Windows.

Archivos DLL importantes

  • COMDLG32.DLL - Controla los cuadros de diálogo.

  • GDI32.DLL - Contiene numerosas funciones para dibujar gráficos, mostrar texto y administrar fuentes.

  • KERNEL32.DLL - Contiene cientos de funciones para la gestión de la memoria y varios procesos.

  • USER32.DLL- Contiene numerosas funciones de interfaz de usuario. Involucrado en la creación de ventanas de programa y sus interacciones entre sí.

Primero, discutiremos los problemas y los requisitos que debe considerar al desarrollar sus propias DLL.

Tipos de DLL

Cuando carga una DLL en una aplicación, dos métodos de vinculación le permiten llamar a las funciones de la DLL exportada. Los dos métodos de vinculación son:

  • vinculación dinámica en tiempo de carga, y
  • 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 realiza llamadas explícitas a las funciones DLL exportadas como funciones locales. Para utilizar la vinculación dinámica en tiempo de carga, proporcione un archivo de encabezado (.h) y un archivo de biblioteca de importación (.lib) cuando compile y vincule la aplicación. Cuando haga esto, el vinculador proporcionará al sistema la información necesaria para cargar la DLL y resolver las ubicaciones de las funciones de DLL exportadas en el momento de la carga.

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

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

La siguiente lista describe los criterios de aplicación para elegir entre la vinculación dinámica en tiempo de carga y la vinculación dinámica en tiempo de ejecución:

  • Startup performance : Si el rendimiento de inicio inicial de la aplicación es importante, debe utilizar la vinculación dinámica en tiempo de ejecución.

  • Ease of use: En el enlace dinámico en tiempo de carga, las funciones DLL exportadas son como funciones locales. Le ayuda a llamar a estas funciones fácilmente.

  • Application logic: En el enlace dinámico en tiempo de ejecución, una aplicación puede ramificarse para cargar diferentes módulos según sea necesario. Esto es importante cuando desarrolla versiones en varios idiomas.

El punto de entrada de DLL

Cuando crea una DLL, puede especificar opcionalmente una función de punto de entrada. La función de punto de entrada se llama cuando los procesos o subprocesos se adjuntan a la DLL o se desconectan de la DLL. Puede utilizar la función de punto de entrada para inicializar o destruir estructuras de datos según lo requiera la DLL.

Además, si la aplicación es multiproceso, puede usar el almacenamiento local de subprocesos (TLS) para asignar memoria que es privada para cada subproceso en la función de punto de entrada. El siguiente código 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, 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 FALSO, la aplicación no se iniciará si está utilizando un enlace dinámico en tiempo de carga. Si está utilizando un enlace dinámico en tiempo de ejecución, solo no se cargará la DLL individual.

La función de punto de entrada solo debe realizar tareas de inicialización simples y no debe llamar a ninguna otra función de carga o terminación de DLL. Por ejemplo, en la función de punto de entrada, no debe llamar directa o indirectamente alLoadLibrary función o la LoadLibraryExfunción. Además, no debe llamar alFreeLibrary función cuando el proceso está terminando.

WARNING: En aplicaciones multiproceso, asegúrese de que el acceso a los datos globales de la DLL esté sincronizado (seguro para subprocesos) para evitar posibles daños en los datos. Para hacer esto, use TLS para proporcionar datos únicos para cada hilo.

Exportación de funciones DLL

Para exportar funciones DLL, puede agregar una palabra clave de función a las funciones DLL exportadas o crear un archivo de definición de módulo (.def) que enumere las funciones DLL exportadas.

Para utilizar una palabra clave de función, debe declarar cada función que desee exportar con la siguiente palabra clave:

__declspec(dllexport)

Para utilizar funciones DLL exportadas en la aplicación, debe declarar cada función que desee importar con la siguiente palabra clave:

__declspec(dllimport)

Normalmente, usaría un archivo de encabezado que tenga define declaración y una ifdef declaración para separar la declaración de exportación y la declaración de importación.

También puede utilizar un archivo de definición de módulo para declarar funciones DLL exportadas. Cuando usa un archivo de definición de módulo, no tiene que agregar la palabra clave de función a las funciones DLL exportadas. En el archivo de definición del módulo, declara elLIBRARY declaración y el EXPORTSdeclaración para la DLL. El siguiente código es un ejemplo de un archivo de definición.

// SampleDLL.def
//
LIBRARY "sampleDLL"

EXPORTS
   HelloWorld

Escribir una DLL de muestra

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

El código siguiente es un ejemplo de un archivo DLL que se creó en Visual C ++ mediante el tipo de proyecto Biblioteca de vínculos dinámicos de Win32.

// 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

Llamar a una DLL de muestra

El código siguiente es un ejemplo de un proyecto de aplicación Win32 que llama a la función 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;
}

NOTE : 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, utiliza un código similar al código siguiente para llamar a la función DLL exportada 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);
}
...

Cuando compila y vincula la aplicación SampleDLL, el sistema operativo Windows busca la DLL SampleDLL en las siguientes ubicaciones en este orden:

  • La carpeta de la aplicación

  • La carpeta actual

  • La carpeta del sistema de Windows (la GetSystemDirectory devuelve la ruta de la carpeta del sistema de Windows).

  • La carpeta de Windows (la GetWindowsDirectory devuelve la ruta de la carpeta de Windows).

Para utilizar una DLL, debe registrarse con las referencias adecuadas ingresadas en el Registro. A veces sucede que una referencia del Registro se daña y las funciones de la DLL ya no se pueden usar. La DLL se puede volver a registrar abriendo Start-Run e ingresando el siguiente comando:

regsvr32 somefile.dll

Este comando asume que algún archivo.dll está en un directorio o carpeta que está en la RUTA. De lo contrario, se debe utilizar la ruta completa de la DLL. También se puede anular el registro de un archivo DLL mediante el interruptor "/ u" como se muestra a continuación.

regsvr32 /u somefile.dll

Esto se puede utilizar para activar y desactivar un servicio.

Hay varias herramientas disponibles para ayudarlo a solucionar problemas de DLL. Algunos de ellos se comentan a continuación.

Caminante de la dependencia

La herramienta Dependency Walker (depends.exe) puede buscar de forma recursiva todas las DLL dependientes que utiliza un programa. Cuando abre un programa en Dependency Walker, Dependency Walker realiza las siguientes comprobaciones:

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

Al utilizar Dependency Walker, puede documentar todas las DLL que utiliza un programa. Puede ayudar a prevenir y corregir problemas de DLL que puedan ocurrir en el futuro. Dependency Walker se encuentra en el siguiente directorio cuando instala Microsoft Visual Studio 6.0:

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

Solucionador de problemas universal DLL

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

  • Dlister.exe - Esta utilidad enumera todos los archivos DLL de la computadora 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 se enumeran 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 que se crean mediante la utilidad Dlister.exe y la utilidad Dcomp.exe en la base de datos dllHell.

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

Tenga en cuenta los siguientes consejos al escribir una DLL:

  • Utilice la convención de llamada adecuada (C o stdcall).

  • Tenga en cuenta el orden correcto de los argumentos pasados ​​a la función.

  • NUNCA cambie el tamaño de las matrices o concatene cadenas utilizando los argumentos pasados ​​directamente a una función. Recuerde, los parámetros que pasa son datos de LabVIEW. Cambiar el tamaño de una matriz o de una cadena puede resultar en un bloqueo al sobrescribir otros datos almacenados en la memoria de LabVIEW. PUEDE cambiar el tamaño de las matrices o concatenar cadenas si pasa un LabVIEW Array Handle o LabVIEW String Handle y está utilizando el compilador Visual C ++ o el compilador Symantec para compilar su DLL.

  • Mientras pasa cadenas a una función, seleccione el tipo correcto de cadena para pasar. Manejador de cadena en C o Pascal o LabVIEW.

  • Las cadenas de Pascal están limitadas a 255 caracteres de longitud.

  • Las cadenas C terminan en NULL. Si su función DLL devuelve datos numéricos en un formato de cadena binaria (por ejemplo, a través de GPIB o el puerto serie), puede devolver valores NULL como parte de la cadena de datos. En tales casos, pasar matrices de enteros cortos (8 bits) es más confiable.

  • Si está trabajando con matrices o cadenas de datos, SIEMPRE pase un búfer o matriz que sea lo suficientemente grande como para contener cualquier resultado colocado en el búfer por la función a menos que los esté pasando como identificadores de LabVIEW, en cuyo caso puede cambiar su tamaño usando CIN funciona bajo el compilador Visual C ++ o Symantec.

  • Enumere las funciones de DLL en la sección EXPORTACIONES del archivo de definición del módulo si está utilizando _stdcall.

  • Enumere las funciones DLL que otras aplicaciones llaman en la sección EXPORTS del archivo de definición de módulo o para incluir la palabra clave _declspec (dllexport) en la declaración de función.

  • Si usa un compilador de C ++, exporte las funciones con la instrucción .C. {} Externa en su archivo de encabezado para evitar alterar los nombres.

  • Si está escribiendo su propia DLL, no debería volver a compilar una DLL mientras otra aplicación la carga en la memoria. Antes de volver a compilar una DLL, asegúrese de que todas las aplicaciones que utilizan esa DLL en particular se descarguen de la memoria. Asegura que la DLL en sí no se cargue en la memoria. Es posible que no pueda reconstruir correctamente si olvida esto y su compilador no le advierte.

  • Pruebe sus DLL con otro programa para asegurarse de que la función (y la DLL) se comporten correctamente. Probarlo con el depurador de su compilador o un programa C simple en el que puede llamar a una función en una DLL le ayudará a identificar si las posibles dificultades son inherentes a la DLL o LabVIEW relacionadas.

Hemos visto cómo escribir una DLL y cómo crear un programa "Hello World". Ese ejemplo debe haberle dado una idea sobre el concepto básico de crear una DLL.

Aquí, daremos una descripción de cómo crear archivos DLL usando Delphi, Borland C ++ y nuevamente VC ++.

Tomemos estos ejemplos uno por uno.

  • Cómo escribir y llamar a DLL dentro de Delphi

  • Hacer DLL desde Borland C ++ Builder IDE

  • Hacer DLL en Microsoft Visual C ++ 6.0