DLL - Guide rapide

La liaison dynamique est un mécanisme qui relie les applications aux bibliothèques au moment de l'exécution. Les bibliothèques restent dans leurs propres fichiers et ne sont pas copiées dans les fichiers exécutables des applications. Les DLL sont liées à une application lorsque l'application est exécutée, plutôt que lors de sa création. Les DLL peuvent contenir des liens vers d'autres DLL.

Plusieurs fois, les DLL sont placées dans des fichiers avec des extensions différentes telles que .EXE, .DRV ou .DLL.

Avantages de DLL

Vous trouverez ci-dessous quelques avantages liés aux fichiers DLL.

Utilise moins de ressources

Les fichiers DLL ne sont pas chargés dans la RAM avec le programme principal; ils n'occupent pas d'espace sauf si nécessaire. Lorsqu'un fichier DLL est nécessaire, il est chargé et exécuté. Par exemple, tant qu'un utilisateur de Microsoft Word modifie un document, le fichier DLL de l'imprimante n'est pas requis dans la RAM. Si l'utilisateur décide d'imprimer le document, l'application Word entraîne le chargement et l'exécution du fichier DLL de l'imprimante.

Favorise l'architecture modulaire

Une DLL aide à promouvoir le développement de programmes modulaires. Il vous aide à développer des programmes volumineux qui nécessitent plusieurs versions linguistiques ou un programme qui nécessite une architecture modulaire. Un exemple de programme modulaire est un programme de comptabilité comportant de nombreux modules qui peuvent être chargés dynamiquement au moment de l'exécution.

Facilite le déploiement et l'installation

Lorsqu'une fonction dans une DLL nécessite une mise à jour ou un correctif, le déploiement et l'installation de la DLL ne nécessitent pas que le programme soit à nouveau lié à la DLL. De plus, si plusieurs programmes utilisent la même DLL, ils bénéficient tous de la mise à jour ou du correctif. Ce problème peut se produire plus fréquemment lorsque vous utilisez une DLL tierce qui est régulièrement mise à jour ou corrigée.

Les applications et les DLL peuvent se lier automatiquement à d'autres DLL, si la liaison DLL est spécifiée dans la section IMPORTS du fichier de définition de module dans le cadre de la compilation. Sinon, vous pouvez les charger explicitement à l'aide de la fonction Windows LoadLibrary.

Fichiers DLL importants

  • COMDLG32.DLL - Contrôle les boîtes de dialogue.

  • GDI32.DLL - Contient de nombreuses fonctions pour dessiner des graphiques, afficher du texte et gérer les polices.

  • KERNEL32.DLL - Contient des centaines de fonctions pour la gestion de la mémoire et divers processus.

  • USER32.DLL- Contient de nombreuses fonctions d'interface utilisateur. Impliqué dans la création de fenêtres de programmes et leurs interactions les unes avec les autres.

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 au moment de l'exécution.

  • Ease of use: Dans la liaison dynamique au 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 multithreads, 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 qui est 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).

Pour utiliser une DLL, elle doit être enregistrée en faisant entrer les références appropriées dans le registre. Il arrive parfois qu'une référence de registre soit corrompue et que les fonctions de la DLL ne puissent plus être utilisées. La DLL peut être réenregistrée en ouvrant Start-Run et en entrant la commande suivante:

regsvr32 somefile.dll

Cette commande suppose que somefile.dll se trouve dans un répertoire ou un dossier qui se trouve dans le PATH. Sinon, le chemin complet de la DLL doit être utilisé. Un fichier DLL peut également être désenregistré en utilisant le commutateur "/ u" comme indiqué ci-dessous.

regsvr32 /u somefile.dll

Cela peut être utilisé pour activer et désactiver un service.

Plusieurs outils sont disponibles pour vous aider à résoudre les problèmes de DLL. Certains d'entre eux sont décrits ci-dessous.

Dépendance Walker

L'outil Dependency Walker (depends.exe) peut rechercher de manière récursive toutes les DLL dépendantes utilisées par un programme. Lorsque vous ouvrez un programme dans Dependency Walker, Dependency Walker effectue les vérifications suivantes:

  • Vérifie les DLL manquantes.
  • Recherche les fichiers programme ou les DLL non valides.
  • Vérifie que les fonctions d'importation et d'exportation correspondent.
  • Recherche les erreurs de dépendance circulaire.
  • Recherche les modules qui ne sont pas valides car les modules sont destinés à un système d'exploitation différent.

En utilisant Dependency Walker, vous pouvez documenter toutes les DLL qu'un programme utilise. Cela peut aider à prévenir et à corriger les problèmes de DLL susceptibles de survenir à l'avenir. Dependency Walker se trouve dans le répertoire suivant lorsque vous installez Microsoft Visual Studio 6.0:

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

Solutionneur de problèmes universel DLL

L'outil DLL Universal Problem Solver (DUPS) est utilisé pour auditer, comparer, documenter et afficher les informations DLL. La liste suivante décrit les utilitaires qui composent l'outil DUPS:

  • Dlister.exe - Cet utilitaire énumère toutes les DLL de l'ordinateur et enregistre les informations dans un fichier texte ou dans un fichier de base de données.

  • Dcomp.exe - Cet utilitaire compare les DLL répertoriées dans deux fichiers texte et produit un troisième fichier texte contenant les différences.

  • Dtxt2DB.exe - Cet utilitaire charge les fichiers texte créés à l'aide de l'utilitaire Dlister.exe et de l'utilitaire Dcomp.exe dans la base de données dllHell.

  • DlgDtxt2DB.exe - Cet utilitaire fournit une version d'interface utilisateur graphique (GUI) de l'utilitaire Dtxt2DB.exe.

Gardez les conseils suivants à l'esprit lors de l'écriture d'une DLL:

  • Utilisez la convention d'appel appropriée (C ou stdcall).

  • Soyez conscient de l'ordre correct des arguments passés à la fonction.

  • NE JAMAIS redimensionner les tableaux ou concaténer des chaînes en utilisant les arguments passés directement à une fonction. N'oubliez pas que les paramètres que vous transmettez sont des données LabVIEW. La modification de la taille des tableaux ou des chaînes peut entraîner une panne en écrasant d'autres données stockées dans la mémoire LabVIEW. Vous POUVEZ redimensionner des tableaux ou concaténer des chaînes si vous passez un descripteur de tableau LabVIEW ou un descripteur de chaîne LabVIEW et que vous utilisez le compilateur Visual C ++ ou Symantec pour compiler votre DLL.

  • Lorsque vous passez des chaînes à une fonction, sélectionnez le type de chaîne correct à transmettre. C ou Pascal ou LabVIEW string Handle.

  • Les chaînes Pascal sont limitées à 255 caractères.

  • Les chaînes C sont terminées par NULL. Si votre fonction DLL renvoie des données numériques dans un format de chaîne binaire (par exemple, via GPIB ou le port série), elle peut renvoyer des valeurs NULL dans le cadre de la chaîne de données. Dans de tels cas, la transmission de tableaux d'entiers courts (8 bits) est la plus fiable.

  • Si vous travaillez avec des tableaux ou des chaînes de données, transmettez TOUJOURS un tampon ou un tableau suffisamment grand pour contenir tous les résultats placés dans le tampon par la fonction à moins que vous ne les transmettiez en tant que poignées LabVIEW, auquel cas vous pouvez les redimensionner à l'aide de CIN fonctions sous Visual C ++ ou compilateur Symantec.

  • Répertoriez les fonctions DLL dans la section EXPORTS du fichier de définition de module si vous utilisez _stdcall.

  • Répertoriez les fonctions DLL que d'autres applications appellent dans la section EXPORTS du fichier de définition de module ou pour inclure le mot clé _declspec (dllexport) dans la déclaration de fonction.

  • Si vous utilisez un compilateur C ++, exportez les fonctions avec l'instruction extern .C. {} Dans votre fichier d'en-tête afin d'éviter toute modification des noms.

  • Si vous écrivez votre propre DLL, vous ne devez pas recompiler une DLL pendant que la DLL est chargée dans la mémoire par une autre application. Avant de recompiler une DLL, assurez-vous que toutes les applications utilisant cette DLL particulière sont déchargées de la mémoire. Il garantit que la DLL elle-même n'est pas chargée dans la mémoire. Vous risquez de ne pas reconstruire correctement si vous oubliez cela et que votre compilateur ne vous avertit pas.

  • Testez vos DLL avec un autre programme pour vous assurer que la fonction (et la DLL) se comportent correctement. Le tester avec le débogueur de votre compilateur ou un simple programme C dans lequel vous pouvez appeler une fonction dans une DLL vous aidera à identifier si d'éventuelles difficultés sont inhérentes à la DLL ou à LabVIEW.

Nous avons vu comment écrire une DLL et comment créer un programme "Hello World". Cet exemple doit vous avoir donné une idée du concept de base de la création d'une DLL.

Ici, nous allons donner une description de la création de DLL en utilisant Delphi, Borland C ++ et encore VC ++.

Prenons ces exemples un par un.