Qu’est-ce qu’une bibliothèque DLL ?

Cet article explique ce qu’est une bibliothèque de liens dynamiques (DLL) et les différents problèmes que vous pouvez rencontrer lorsque vous utilisez des DLL. Il explique également quelques problèmes avancés que vous devez prendre en compte quand vous développez vos propres DLL.

Applicabilité : Windows 10 - Toutes les éditions
Numéro de l’article d’origine dans la base de connaissances : 815065

Résumé

Pour décrire une DLL, cet article explique des méthodes de liaison dynamique, les dépendances des DLL, les points d’entrée de DLL, l’exportation des fonctions de DLL et les outils de dépannage de DLL.

Cet article termine par une comparaison approfondie entre les DLL et les assemblys Microsoft .NET Framework.

Pour les systèmes d’exploitation Windows, la plupart des fonctionnalités du système d’exploitation sont fournies par des DLL. En outre, lorsque vous exécutez un programme sous l’un de ces systèmes d’exploitation Windows, une grande partie des fonctionnalités du programme peut être fournie par les DLL. Par exemple, certains programmes peuvent contenir beaucoup de modules différents, chaque module du programme est alors contenu et distribué dans des DLL.

L’utilisation des DLL contribue à favoriser la modularisation de code, la réutilisation de code, une utilisation efficace de la mémoire et la réduction de l’espace disque utilisé. Par conséquent, le système d’exploitation et les programmes se chargent et s’exécutent plus rapidement et ils occupent moins d’espace disque sur l’ordinateur.

Lorsqu’un programme utilise une DLL, un problème appelé « dépendance » peut empêcher l’exécution du programme. Lorsqu’un programme utilise une DLL, une dépendance est créée. Si un autre programme remplace et rompt cette dépendance, le programme d’origine ne peut pas fonctionner correctement.

Avec la publication de .NET Framework, la plupart des problèmes de dépendance ont été éliminés grâce à des assemblys.

Informations supplémentaires

Une DLL est une bibliothèque qui contient du code et des données pouvant être utilisés simultanément par plusieurs programmes. Par exemple, dans les systèmes d’exploitation Windows, la DLL Comdlg32 exécute des fonctions liées aux boîtes de dialogue communes. Chaque programme peut utiliser la fonctionnalité contenue dans cette DLL pour implémenter une boîte de dialogue Ouvrir. Cela permet de promouvoir la réutilisation de code et l’utilisation efficace de la mémoire.

En utilisant une DLL, un programme peut être organisé en plusieurs modules dans des composants distincts. Par exemple, un programme de comptabilité peut être vendu par modules. Chaque module peut être chargé dans le programme principal au moment de l’exécution si ce module est installé. Étant donné que les modules sont distincts, le temps de chargement du programme est réduit. De plus, un module n’est chargé que quand cette fonctionnalité est demandée.

En outre, les mises à jour sont plus faciles à appliquer à chaque module, sans que cela n’affecte les autres parties du programme. Par exemple, vous pouvez posséder un programme de gestion des salaires et les taux d’imposition changent chaque année. Lorsque ces modifications sont isolées dans une DLL, vous pouvez appliquer une mise à jour sans devoir générer ou installer de nouveau l’ensemble du programme.

La liste suivante décrit quelques-uns des fichiers implémentés comme DLL dans les systèmes d’exploitation Windows :

  • Fichiers de contrôles ActiveX (.ocx)

    Un contrôle ActiveX est, par exemple, un contrôle Calendrier qui vous permet de sélectionner une date dans un calendrier.

  • Fichiers du Panneau de configuration (.cpl)

    Un fichier .cpl peut être un élément qui se trouve dans le Panneau de configuration. Chaque élément est une DLL spécialisée.

  • Fichiers de pilote de périphérique (.drv)

    Un pilote de périphérique est par exemple un pilote d’imprimante qui contrôle l’impression vers une imprimante.

Avantages des DLL

La liste suivante décrit quelques-uns des avantages offerts lorsqu’un programme utilise une DLL :

  • Le programme utilise moins de ressources

    Lorsque plusieurs programmes utilisent la même bibliothèque de fonctions, une DLL peut réduire la duplication du code qui est chargé sur le disque et dans la mémoire physique. Cela peut influencer considérablement les performances non seulement du programme fonctionnant au premier plan, mais également des autres programmes qui s’exécutent sur le système d’exploitation Windows.

  • Une DLL favorise une architecture modulaire

    Une DLL favorise le développement de programmes modulaires. Cela vous permet de développer des programmes volumineux qui requièrent plusieurs versions linguistiques ou un programme qui nécessite une architecture modulaire. Un exemple d’un programme modulaire est un programme de comptabilité comprenant de nombreux modules qui peuvent être chargés dynamiquement au moment de l’exécution.

  • Une DLL facilite le déploiement et l’installation

    Lorsqu’une fonction dans une DLL a besoin d’une mise à jour ou d’un correctif, le déploiement et l’installation de la DLL ne nécessitent pas la recréation du lien entre le programme et la DLL. En outre, si plusieurs programmes utilisent la même DLL, ces programmes bénéficieront tous de la mise à jour ou du correctif. Ce problème peut se produire plus fréquemment lorsque vous utilisez une DLL d’un fournisseur tiers qui est mise à jour ou corrigée régulièrement.

Dépendances des DLL

Lorsqu’un programme ou une DLL utilise une fonction DLL dans une autre DLL, une dépendance est créée. Le programme n’est plus indépendant et il peut rencontrer des problèmes si la dépendance est rompue. Par exemple, le programme ne peut pas s’exécuter dans l’une des situations suivantes :

  • Une DLL dépendante est mise à niveau vers une nouvelle version.
  • Une DLL dépendante est corrigée.
  • Une DLL dépendante est remplacée par une version antérieure.
  • Une DLL dépendante est supprimée de l’ordinateur.

Ces situations sont appelées « conflits de DLL ». Si la compatibilité en amont n’est pas assurée, le programme peut ne pas s’exécuter correctement.

La liste suivante décrit les modifications qui ont été introduites dans Windows 2000 et les systèmes d’exploitation Windows ultérieurs pour réduire les problèmes de dépendance :

  • Protection des fichiers Windows

    Dans la fonctionnalité Protection des fichiers Windows, le système d’exploitation empêche la mise à jour ou la suppression des DLL système par un agent non autorisé. Lorsque l’installation d’un programme essaie de supprimer ou de mettre à jour une DLL qui est définie comme DLL système, la fonctionnalité Protection des fichiers Windows recherche une signature numérique valide.

  • DLL privées

    Les DLL privées vous permettent d’isoler un programme des modifications qui sont apportées aux DLL partagées. Les DLL privées utilisent des informations spécifiques à la version ou un fichier .local vide pour appliquer la version de la DLL utilisée par le programme. Pour utiliser des DLL privées, localisez vos DLL dans le dossier racine des programmes. Ensuite, pour les nouveaux programmes, ajoutez à la DLL des informations spécifiques à la version. Pour les programmes anciens, utilisez un fichier .local vide. Chaque méthode ordonne au système d’exploitation d’utiliser les DLL privées situées dans le dossier racine de programmes.

Outils de dépannage de DLL

Il existe plusieurs outils permettant de résoudre les problèmes liés aux DLL. Voici quelques-uns de ces outils :

Dependency Walker

L’outil Dependency Walker permet d’effectuer une analyse récursive de toutes les DLL dépendantes utilisées par un programme. Quand vous ouvrez un programme dans Dependency Walker, il effectue les contrôles suivants :

  • Dependency Walker recherche les DLL manquantes.
  • Il recherche les fichiers programmes ou les DLL qui ne sont pas valides.
  • Il vérifie que les fonctions d’importation et les fonctions d’exportation correspondent.
  • Il recherche les erreurs de dépendance circulaire.
  • Il recherche les modules qui ne sont pas valides car ils appartiennent à un autre système d’exploitation.

Grâce à Dependency Walker, vous pouvez documenter toutes les DLL utilisées par un programme. Cela peut permettre d’empêcher et de corriger les problèmes de DLL qui peuvent se produire dans le futur. Dependency Walker est situé dans le répertorie suivant lors de l’installation de Visual Studio 6.0 :

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

DLL Universal Problem Solver

L’outil DLL Universal Problem Solver (DUPS) permet d’auditer, de comparer, de documenter et d’afficher les informations sur les DLL. La liste suivante décrit les utilitaires contenus dans l’outil DUPS :

  • Dlister.exe

    Cet utilitaire énumère toutes les DLL sur l’ordinateur et enregistre ces informations dans un fichier texte ou un fichier de base de données.

  • Dcomp.exe

    Cet utilitaire compare les DLL répertoriées dans deux fichiers texte et génère un troisième fichier texte indiquant les différences.

  • Dtxt2DB.exe

    Cet utilitaire charge les fichiers texte qui sont créés avec les utilitaires Dlister.exe et Dcomp.exe dans la base de données dllHell.

  • DlgDtxt2DB.exe

    Cet utilitaire fournit une version GUI (interface utilisateur graphique) de l’utilitaire Dtxt2DB.exe.

Base de données d’aide sur les DLL

La base de données d’aide sur les DLL vous aide à localiser des versions spécifiques de DLL qui sont installées par les logiciels Microsoft.

Développement de DLL

Cette section décrit les problèmes et les exigences à prendre en compte lorsque vous développez vos propres DLL.

Types de DLL

Lorsque vous chargez une DLL dans une application, vous pouvez appeler les fonctions DLL exportées à l’aide de deux méthodes de liaison : la liaison dynamique au moment du chargement et la liaison dynamique au moment de l’exécution.

Liaison dynamique au moment du chargement

Dans la liaison dynamique au moment du chargement, une application effectue des appels explicites à des fonctions DLL exportées comme des 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. Lors de cette opération, l’éditeur de liens fournit au système les informations requises pour charger la DLL et définir les emplacements de fonction DLL exportée au moment du chargement.

Liaison dynamique au moment de l’exécution (en anglais)

Dans la liaison dynamique au moment de l’exécution, une application appelle la fonction LoadLibrary ou LoadLibraryEx pour charger la DLL au moment de l’exécution. Une fois la DLL 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 au moment de l’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 savoir quand utiliser la liaison dynamique au moment du chargement et quand utiliser la liaison dynamique au moment de l’exécution :

  • Performance au démarrage

    Si les performances au démarrage initial de l’application sont importantes, vous devez utiliser la liaison dynamique au moment de l’exécution.

  • Facilité d’utilisation

    Dans la liaison dynamique au moment du chargement, les fonctions DLL exportées sont similaires aux fonctions locales. Il est ainsi facile pour vous d’appeler ces fonctions.

  • Logique d’application

    Dans la liaison dynamique au moment de l’exécution, une application peut choisir de charger tels ou tels modules, selon les besoins. Ceci est important quand vous développez des versions multilingues.

Point d’entrée de 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 des structures de données ou en détruire, selon les besoins de la DLL. En outre, si l’application est multithread, vous pouvez utiliser le stockage local des threads (TLS) dans la fonction de point d’entrée pour allouer de la mémoire privée à chaque thread. Le code suivant est un exemple de fonction de point d’entrée 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;
}

Quand 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 au moment de l’exécution, seule la DLL individuelle ne se chargera pas.

La fonction de point d’entrée doit uniquement effectuer des tâches d’initialisation simples et ne doit pas appeler une autre fonction de chargement ou d’arrêt de DLL. Par exemple, dans la fonction de point d’entrée, vous ne devez ni directement ni indirectement appeler la fonction LoadLibrary ou LoadLibraryEx. En outre, vous ne devez pas appeler la fonction FreeLibrary quand le processus se termine.

Remarque

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

Exporter 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 à exporter à l’aide du mot clé suivant :
__declspec(dllexport)

Pour utiliser des fonctions DLL exportées dans l’application, vous devez déclarer chaque fonction à importer à l’aide du mot clé suivant : __declspec(dllimport).

En règle générale, vous utilisez un fichier d’en-tête contenant une instruction define et une instruction ifdef pour séparer l’instruction export et l’instruction import.

Vous pouvez également utiliser un fichier de définition de module pour déclarer des fonctions DLL exportées. Lorsque vous utilisez un tel fichier, vous ne devez pas ajouter de mot clé de fonction aux fonctions DLL exportées. Dans le fichier de définition de module, vous déclarez les instructions LIBRARY et EXPORTS pour la DLL. Le code suivant constitue un exemple de fichier de définition :

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

Exemple de DLL et d’application

Dans Visual C++ 6.0, vous pouvez créer une DLL en sélectionnant le type de projet Bibliothèque de liens dynamiques Win32 ou MFC AppWizard (dll).

Le code suivant est un exemple d’une DLL qui a été créée dans Visual C++ en utilisant le type de projet Bibliothèque de liens dynamiques 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

Le code suivant est un exemple d’un projet 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;
}

Remarque

Dans la liaison dynamique au moment du chargement, vous devez lier la bibliothèque d’importation SampleDLL.lib qui est créée lors de la génération du projet SampleDLL.

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

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

  1. Le dossier d’application

  2. Le dossier actif

  3. Le dossier système de Windows

    Remarque

    La fonction GetSystemDirectory renvoie le chemin du dossier système de Windows.

  4. Le dossier Windows

    Remarque

    La fonction GetWindowsDirectory renvoie le chemin du dossier Windows.

Assembly .NET Framework

Avec l’introduction de .NET et de .NET Framework, la plupart des problèmes associés aux DLL ont été éliminés à l’aide des assemblys. Un assembly est une unité logique de fonctionnalités qui s’exécutent sous le contrôle du Common Language Runtime .NET (CLR). Un assembly existe physiquement en tant que fichier .dll ou .exe. Toutefois, un assembly est intrinsèquement différent d’une DLL Microsoft Win32.

Un fichier d’assembly contient un manifeste de l’assembly, des métadonnées de type, un code Microsoft Intermediate Language (MSIL) et d’autres ressources. Le manifeste de l’assembly contient les métadonnées de l’assembly fournissant toutes les informations nécessaires pour qu’un assembly soit autodescriptif. Les informations suivantes sont incluses dans le manifeste de l’assembly :

  • Nom de l’assembly
  • Informations de version
  • Informations de culture
  • Informations sur les noms forts
  • Liste de fichiers de l’assembly
  • Des informations de référence de type
  • Des informations sur les assemblys dépendants et référencés

Le code MSIL contenu dans l’assembly ne peut pas être exécuté directement. Son exécution est gérée par le biais du CLR. Par défaut, lorsque vous créez un assembly, il est privé à l’application. Créer un assembly partagé nécessite l’affectation d’un nom fort à l’assembly puis la publication de l’assembly dans le Global Assembly Cache.

La liste suivante compare quelques-unes des fonctionnalités d’assemblys par rapport aux fonctionnalités des DLL Win32 :

  • Autodescriptif

    Lorsque vous créez un assembly, toutes les informations requises par le CLR pour l’exécuter sont contenues dans le manifeste de l’assembly. Ce manifeste contient une liste des assemblys dépendants. Ainsi, le CLR peut maintenir la cohérence des assemblys utilisés dans l’application. Dans les DLL Win32, vous ne pouvez pas maintenir de cohérence dans un ensemble de DLL utilisées dans une application lorsque vous utilisez des DLL partagées.

  • Gestion des versions

    Dans un manifeste d’assembly, les informations de version sont enregistrées et sont appliquées par le CLR. Les stratégies de version vous permettent en outre de mettre en vigueur une utilisation spécifique à la version. Dans les DLL Win32, le contrôle de version ne peut pas être mis en œuvre par le système d’exploitation. Vous devez vous assurer que les DLL sont à compatibilité descendante.

  • Déploiement côte à côte

    Les assemblys prennent en charge le déploiement côte à côte. Une application peut utiliser une version d’un assembly et une autre application une version différente du même assembly. Apparu dans Windows 2000, le déploiement côte à côte est pris en charge en plaçant des DLL dans le dossier d’application. En outre, la fonctionnalité Protection des fichiers Windows empêche le remplacement des DLL système par un agent non autorisé.

  • Autonomie et isolation

    Une application qui est développée en utilisant un assembly peut être autonome et isolée des autres applications qui s’exécutent sur l’ordinateur. Cette caractéristique vous aide à créer des installations à impact nul.

  • Exécution

    Un assembly est exécuté sous les autorisations de sécurité fournies dans le manifeste de l’assembly et qui sont contrôlées par le CLR.

  • Indépendance par rapport au langage

    Un assembly peut être développé en utilisant n’importe lequel des langages pris en charge par .NET. Par exemple, vous pouvez développer un assembly dans Microsoft Visual C#, puis utiliser cet assembly dans un projet Visual Basic .NET.

Collecte de données

Si vous avez besoin de l’aide du support Microsoft, nous vous recommandons de collecter les informations en suivant les étapes mentionnées dans Collecter des informations à l’aide de TSS pour les problèmes liés au déploiement.

References