La liaison à un fichier DLL peut se produire implicitement au moment de la liaison de compilation , ou explicitement au moment de l'exécution. Dans tous les cas, la DLL finit par être chargée dans l'espace mémoire des processus et tous ses points d'entrée exportés sont disponibles pour l'application.
S'il est utilisé explicitement au moment de l'exécution, vous utilisez LoadLibrary()
et GetProcAddress()
pour charger manuellement la DLL et obtenir des pointeurs vers les fonctions que vous devez appeler.
S'ils sont liés implicitement lors de la génération du programme, les stubs pour chaque exportation DLL utilisée par le programme sont liés au programme à partir d'une bibliothèque d'importation, et ces stubs sont mis à jour lorsque l'EXE et la DLL sont chargés au lancement du processus. (Oui, j'ai simplifié plus d'un peu ici ...)
Ces stubs doivent provenir de quelque part, et dans la chaîne d'outils Microsoft, ils proviennent d'une forme spéciale de fichier .LIB appelée bibliothèque d'importation . Le .LIB requis est généralement généré en même temps que la DLL et contient un stub pour chaque fonction exportée à partir de la DLL.
De façon confuse, une version statique de la même bibliothèque serait également livrée sous forme de fichier .LIB. Il n'y a pas de moyen simple de les distinguer, sauf que les LIB qui sont des bibliothèques d'importation pour les DLL seront généralement plus petites (souvent beaucoup plus petites) que la LIB statique correspondante.
Si vous utilisez la chaîne d'outils GCC, par ailleurs, vous n'avez pas besoin de bibliothèques d'importation pour correspondre à vos DLL. La version de l'éditeur de liens Gnu portée sur Windows comprend directement les DLL et peut synthétiser la plupart des stubs requis à la volée.
Mise à jour
Si vous ne pouvez pas résister à savoir où se trouvent réellement tous les écrous et boulons et ce qui se passe réellement, il y a toujours quelque chose chez MSDN pour vous aider. L'article de Matt Pietrek Un examen approfondi du format de fichier exécutable portable Win32 est un aperçu très complet du format du fichier EXE et de la façon dont il est chargé et exécuté. Il a même été mis à jour pour couvrir .NET et plus depuis son apparition dans MSDN Magazine ca. 2002.
En outre, il peut être utile de savoir comment savoir exactement quelles DLL sont utilisées par un programme. L'outil pour cela est Dependency Walker, aka depend.exe. Une version de celui-ci est incluse avec Visual Studio, mais la dernière version est disponible auprès de son auteur à l' adresse http://www.dependencywalker.com/ . Il peut identifier toutes les DLL qui ont été spécifiées au moment de la liaison (chargement anticipé et chargement différé) et il peut également exécuter le programme et surveiller les DLL supplémentaires qu'il charge au moment de l'exécution.
Mise à jour 2
J'ai reformulé une partie du texte précédent pour le clarifier lors de la relecture, et pour utiliser les termes de l'art des liens implicites et explicites pour la cohérence avec MSDN.
Ainsi, nous avons trois façons de rendre les fonctions de bibliothèque disponibles pour être utilisées par un programme. La question de suivi évidente est alors: "Comment choisir quelle voie?"
La liaison statique est la façon dont la majeure partie du programme lui-même est liée. Tous vos fichiers objets sont répertoriés et rassemblés dans le fichier EXE par l'éditeur de liens. En cours de route, l'éditeur de liens s'occupe de tâches mineures comme la correction des références aux symboles globaux afin que vos modules puissent appeler les fonctions de l'autre. Les bibliothèques peuvent également être liées statiquement. Les fichiers objets qui composent la bibliothèque sont rassemblés par un bibliothécaire dans un fichier .LIB dans lequel l'éditeur de liens recherche les modules contenant les symboles nécessaires. L'un des effets de la liaison statique est que seuls les modules de la bibliothèque utilisés par le programme y sont liés; les autres modules sont ignorés. Par exemple, la bibliothèque mathématique C traditionnelle comprend de nombreuses fonctions de trigonométrie. Mais si vous liez contre et utilisezcos()
, vous ne vous retrouvez pas avec une copie du code pour sin()
ou à tan()
moins que vous n'ayez également appelé ces fonctions. Pour les grandes bibliothèques avec un ensemble riche de fonctionnalités, cette inclusion sélective de modules est importante. Sur de nombreuses plates-formes telles que les systèmes embarqués, la taille totale du code disponible pour une utilisation dans la bibliothèque peut être importante par rapport à l'espace disponible pour stocker un exécutable dans l'appareil. Sans inclusion sélective, il serait plus difficile de gérer les détails des programmes de construction pour ces plates-formes.
Cependant, avoir une copie de la même bibliothèque dans chaque programme en cours d'exécution crée un fardeau pour un système qui exécute normalement de nombreux processus. Avec le bon type de système de mémoire virtuelle, les pages de mémoire qui ont un contenu identique ne doivent exister qu'une seule fois dans le système, mais peuvent être utilisées par de nombreux processus. Cela crée un avantage pour augmenter les chances que les pages contenant du code soient probablement identiques à certaines pages dans autant d'autres processus en cours que possible. Mais, si les programmes se lient statiquement à la bibliothèque d'exécution, chacun a un mélange différent de fonctions, chacune disposée dans ce qui traite la carte mémoire à différents endroits, et il n'y a pas beaucoup de pages de code partageables à moins que ce ne soit un programme qui est à lui seul courir dans plus que processus. L'idée d'une DLL a donc gagné un autre avantage majeur.
Une DLL pour une bibliothèque contient toutes ses fonctions, prêtes à être utilisées par n'importe quel programme client. Si de nombreux programmes chargent cette DLL, ils peuvent tous partager ses pages de codes. Tout le monde y gagne. (Eh bien, jusqu'à ce que vous mettiez à jour une DLL avec une nouvelle version, mais cela ne fait pas partie de cette histoire. Google DLL Hell pour ce côté de l'histoire.)
Donc, le premier grand choix à faire lors de la planification d'un nouveau projet est entre la liaison dynamique et statique. Avec la liaison statique, vous avez moins de fichiers à installer et vous êtes à l'abri des tiers mettant à jour une DLL que vous utilisez. Cependant, votre programme est plus volumineux et il n'est pas aussi bon citoyen de l'écosystème Windows. Avec la liaison dynamique, vous avez plus de fichiers à installer, vous pourriez avoir des problèmes avec un tiers mettant à jour une DLL que vous utilisez, mais vous êtes généralement plus convivial avec les autres processus du système.
Un gros avantage d'une DLL est qu'elle peut être chargée et utilisée sans recompiler ni même relier le programme principal. Cela peut permettre à un fournisseur de bibliothèque tiers (pensez à Microsoft et à l'environnement d'exécution C, par exemple) de corriger un bogue dans sa bibliothèque et de le distribuer. Une fois qu'un utilisateur final a installé la DLL mise à jour, il bénéficie immédiatement de cette correction de bogue dans tous les programmes qui utilisent cette DLL. (À moins qu'il ne casse les choses. Voir DLL Hell.)
L'autre avantage vient de la distinction entre chargement implicite et explicite. Si vous optez pour l'effort supplémentaire de chargement explicite, la DLL peut même ne pas avoir existé lorsque le programme a été écrit et publié. Cela permet des mécanismes d'extension qui peuvent découvrir et charger des plugins, par exemple.
lib /list xxx.lib
etlink /dump /linkermember xxx.lib
. Consultez cette question sur le débordement de pile .