DLL - Cómo escribir
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 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 la vinculación dinámica 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, declaras 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 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).