DLL - Comment écrire

Tout d'abord, nous discuterons des problèmes et des exigences à prendre en compte lors du développement de vos propres DLL.

Types de DLL

Lorsque vous chargez une DLL dans une application, deux méthodes de liaison vous permettent d'appeler les fonctions DLL exportées. Les deux méthodes de liaison sont -

  • liaison dynamique au moment du chargement, et
  • liaison dynamique à l'exécution.

Liaison dynamique au moment du chargement

Dans la liaison dynamique au moment du chargement, une application effectue des appels explicites aux fonctions DLL exportées comme les fonctions locales. Pour utiliser la liaison dynamique au moment du chargement, fournissez un fichier d'en-tête (.h) et un fichier de bibliothèque d'importation (.lib) lorsque vous compilez et liez l'application. Lorsque vous effectuez cette opération, l'éditeur de liens fournira au système les informations requises pour charger la DLL et résoudre les emplacements des fonctions DLL exportées au moment du chargement.

Liaison dynamique d'exécution

Dans la liaison dynamique à l'exécution, une application appelle la fonction LoadLibrary ou la fonction LoadLibraryEx pour charger la DLL au moment de l'exécution. Une fois la DLL correctement chargée, vous utilisez la fonction GetProcAddress pour obtenir l'adresse de la fonction DLL exportée que vous souhaitez appeler. Lorsque vous utilisez la liaison dynamique d'exécution, vous n'avez pas besoin d'un fichier de bibliothèque d'importation.

La liste suivante décrit les critères d'application pour choisir entre la liaison dynamique au moment du chargement et la liaison dynamique à l'exécution -

  • Startup performance - Si les performances de démarrage initial de l'application sont importantes, vous devez utiliser la liaison dynamique d'exécution.

  • Ease of use- Dans la liaison dynamique au moment du chargement, les fonctions DLL exportées sont comme des fonctions locales. Cela vous aide à appeler ces fonctions facilement.

  • Application logic- Dans la liaison dynamique d'exécution, une application peut créer une branche pour charger différents modules selon les besoins. Ceci est important lorsque vous développez des versions en plusieurs langues.

Le point d'entrée DLL

Lorsque vous créez une DLL, vous pouvez éventuellement spécifier une fonction de point d'entrée. La fonction de point d'entrée est appelée lorsque des processus ou des threads s'attachent à la DLL ou se détachent de la DLL. Vous pouvez utiliser la fonction de point d'entrée pour initialiser ou détruire les structures de données comme requis par la DLL.

En outre, si l'application est multithread, vous pouvez utiliser le stockage local des threads (TLS) pour allouer de la mémoire qui est privée à chaque thread dans la fonction de point d'entrée. Le code suivant est un exemple de la fonction de point d'entrée 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;
}

Lorsque la fonction de point d'entrée renvoie une valeur FALSE, l'application ne démarre pas si vous utilisez la liaison dynamique au moment du chargement. Si vous utilisez la liaison dynamique d'exécution, seule la DLL individuelle ne se chargera pas.

La fonction de point d'entrée ne doit effectuer que des tâches d'initialisation simples et ne doit appeler aucune autre fonction de chargement ou d'arrêt de DLL. Par exemple, dans la fonction de point d'entrée, vous ne devez pas appeler directement ou indirectement leLoadLibrary fonction ou le LoadLibraryExfonction. De plus, vous ne devez pas appeler leFreeLibrary fonction lorsque le processus se termine.

WARNING- Dans les applications multithread, assurez-vous que l'accès aux données globales DLL est synchronisé (thread-safe) pour éviter une éventuelle corruption des données. Pour ce faire, utilisez TLS pour fournir des données uniques pour chaque thread.

Exportation des fonctions DLL

Pour exporter des fonctions DLL, vous pouvez ajouter un mot-clé de fonction aux fonctions DLL exportées ou créer un fichier de définition de module (.def) qui répertorie les fonctions DLL exportées.

Pour utiliser un mot-clé de fonction, vous devez déclarer chaque fonction que vous souhaitez exporter avec le mot-clé suivant -

__declspec(dllexport)

Pour utiliser les fonctions DLL exportées dans l'application, vous devez déclarer chaque fonction que vous souhaitez importer avec le mot-clé suivant -

__declspec(dllimport)

En règle générale, vous utiliseriez un fichier d'en-tête ayant define déclaration et un ifdef instruction pour séparer la déclaration d'exportation et la déclaration d'importation.

Vous pouvez également utiliser un fichier de définition de module pour déclarer les fonctions DLL exportées. Lorsque vous utilisez un fichier de définition de module, vous ne devez pas ajouter le mot clé de fonction aux fonctions DLL exportées. Dans le fichier de définition du module, vous déclarez leLIBRARY déclaration et le EXPORTSinstruction pour la DLL. Le code suivant est un exemple de fichier de définition.

// SampleDLL.def
//
LIBRARY "sampleDLL"

EXPORTS
   HelloWorld

Écrire un exemple de DLL

Dans Microsoft Visual C ++ 6.0, vous pouvez créer une DLL en sélectionnant le Win32 Dynamic-Link Library type de projet ou MFC AppWizard (dll) Type de projet.

Le code suivant est un exemple d'une DLL qui a été créée dans Visual C ++ à l'aide du type de projet 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

Appel d'un exemple de DLL

Le code suivant est un exemple d'un projet d'application Win32 qui appelle la fonction DLL exportée dans 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 - Dans la liaison dynamique au moment du chargement, vous devez lier la bibliothèque d'importation SampleDLL.lib créée lorsque vous générez le projet SampleDLL.

Dans la liaison dynamique à l'exécution, vous utilisez un code similaire au code suivant pour appeler la fonction DLL exportée 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);
}
...

Lorsque vous compilez et liez l'application SampleDLL, le système d'exploitation Windows recherche la DLL SampleDLL aux emplacements suivants dans cet ordre:

  • Le dossier d'application

  • Le dossier actuel

  • Le dossier système Windows (Le GetSystemDirectory renvoie le chemin du dossier système Windows).

  • Le dossier Windows (Le GetWindowsDirectory renvoie le chemin du dossier Windows).