Fonctionnement des DLL dans le Développement C++ sous Windows

Exportation de symboles (__declspec(dllexport))

Lorsque le compilateur C/C++ de Microsoft rencontre un modificateur __declspec(dllexport) appliqué à une variable, un prototype de fonction ou une classe C++, il intègre des informations supplémentaires dans les fichiers .obj générés. Lors de l'édition de liens pour une DLL, le linkeur analyse ces informations pour créer un fichier .lib. Ce fichier .lib répertorie les symboles exportés par la DLL et est requis lors de la liaison des fonctions et variables importées qui référencent ces symboles.

En plus du fichier .lib, le linkeur intègre une table de symboles exportés dans le fichier DLL lui-même. Cette section, triée alphabétiquement, contient les noms des variables, fonctions et classes exportées, ainsi que leurs adresses virtuelles relatives (RVA) indiquant leur emplacement dans le module DLL. L'outil DumpBin.exe avec l'option -exports permet d'inspecter cette table d'exportation.

Exemple de configuration d'exportation

#ifdef APPCORE_EXPORTS
#define APPCORE_API __declspec(dllexport)
#else
#define APPCORE_API __declspec(dllimport)
#endif

class APPCORE_API CAppCore
{
public:
    static wchar_t g_szDebugLog[1024];
    static unsigned long g_ulMainProcessID;
};

Construction d'une DLL et d'un exécutable

Pour une DLL, les fichiers d'en-tête contiennent les déclarations des prototypes de fonctions et structures à exporter, tandis que les fichiers source incluent leurs implémentations. Le compilateur produit un fichier .obj pour chaque source. Lors de l'édition de liens, le linkeur fusionne les fichiers .obj pour générer la DLL ; si au moins un symbole est exporté, un fichier .lib est également créé.

Pour un exécutable, le linkeur combine les fichiers .obj et utilise le fichier .lib pour résoudre les références aux fonctions et variables importées, produisant un fichier .exe avec une table d'importation listant les DLL nécessaires et les symboles importés.

Chargement implicite d'une DLL

Le chargement implicite se fait en utilisant __declspec(dllimport) dans le code source pour les fonctions à importer, ce qui permet au système de charger la DLL automatiquement au démarrage du programme.

Chargement explicite d'une DLL

#include <Windows.h>

typedef int
(WINAPI
    *pfnProcessData)
    (
        const void* pData,
        size_t nSize
    );

static pfnProcessData g_fnProcessor = NULL;
static HMODULE g_hDataLib = NULL;

// Charger la DLL depuis le répertoire courant
g_hDataLib = LoadLibrary(L"DataProc.dll");

// Obtenir l'adresse de la fonction par son nom
g_fnProcessor = (pfnProcessData)GetProcAddress(g_hDataLib, "ProcessInput");

Déchargement explicite d'une DLL

if (g_hDataLib != NULL)
{
    FreeLibrary(g_hDataLib);
    g_hDataLib = NULL;
}

Redirection de fonctions

L'utilisation de directives pragma permet de rediriger les fonctions exportées vers des implémentations dans d'autres DLL. Par exemple, pour exporter une fonction MainFunc qui appelle HelperFunc dans un module externe :

#pragma comment(linker, "/export:MainFunc=HelperModule.HelperFunc")

Chargement différé des DLL

Le chargement différé retarde le chargement d'une DLL jusqu'à ce qu'une fonction qu'elle contient soit effectivement appelée, ce qui peut améliorer les temps de démarrage.

Redirection de DLL

La redirection force le chargeur à rechercher d'abord les DLL dans le répertoire de l'application. Cela est activé en plaçant un fichier nommé AppName.local (où AppName est le nom de l'exécutable) dans le répertoire de l'application.

Ordre de recherche des DLL

  1. Répertoire contenant l'exécutable.
  2. Répertoire système Windows (obtenu via GetSystemDirectory).
  3. Répertoire système 16 bits (sous-répertoire System du répertoire Windows).
  4. Répertoire Windows (obtenu via GetWindowsDirectory).
  5. Répertoire courant du processus.
  6. Répertoires listés dans la variable d'environnement PATH.

Un chargement implicite excessif de DLL peut allonger le temps d'initialisation du programme.

Étiquettes: dll C++ Windows visual-studio linker

Publié le 2 juin à 21h05