Utilisation d'interfaces lorsque vous travaillez avec DLL. Fonctions d'appel à partir de la DLL Télécharger la DLL branchée implicitement

Utilisant Dll (Bibliothèque connectée dynamiquement) est généralisée dans la programmation Windows. Dll En fait, une partie du fichier exécutable avec l'extension Dll. Tout programme peut causer Dll.

Avantage Dll Réside ci-dessous:

  • Réutiliser le code.
  • Partage d'applications de code.
  • Code de séparation.
  • Améliorer la consommation de ressources sous Windows.

Créer une dll

au menu Déposer Sélectionnez Nouveau -\u003e Autre. Dans la boîte de dialogue sur l'onglet Nouveau Choisir Magicien dll. Automatiquement le module sera créé - un modèle vide du futur Dll.

Syntaxe DLL

Pour construire Dll, Sélectionnez Projet -\u003e Construire Nom nom .

La visibilité des fonctions et des procédures

Les fonctions et les procédures peuvent être locales et exportées de Dll.

Local

Les fonctions locales et les procédures peuvent être utilisées à l'intérieur Dll. Ils ne sont visibles que dans la bibliothèque et aucun programme ne peut les appeler à l'extérieur.

Exporté

Les fonctions et procédures exportées peuvent être utilisées à l'extérieur Dll. D'autres programmes peuvent causer des fonctions et des procédures.

Le code source ci-dessus utilise la fonction exportée. Le nom de la fonction suit le mot réservé Exportations..

Télécharger dll

Delphi a deux types de téléchargement Dll:

Chargement statique

Lorsque vous démarrez l'application, chargés automatiquement. Il reste en mémoire tout au long du programme. Très facile à utiliser. Juste ajouter un mot externe Après avoir déclaré une fonction ou une procédure.

Fonction SummaTotal (facteur: Entier): Entier; S'inscrire; externe "exemple.dll";

Si un Dll Ne sera pas trouvé, le programme continuera à fonctionner.

Chargement en mémoire selon vos besoins. Sa mise en œuvre est plus compliquée, car vous devez vous-même télécharger et décharger de la mémoire. La mémoire est utilisée plus économiquement, la demande fonctionne donc plus rapidement. Le programmeur doit suivre, de sorte que tout fonctionnait correctement. Pour cela, vous avez besoin de:

  • Annoncez le type de fonction ou de la procédure décrite.
  • Chargez la bibliothèque en mémoire.
  • Obtenez l'adresse de la fonction ou de la procédure en mémoire.
  • Appelez une fonction ou une procédure.
  • Décharger la bibliothèque de la mémoire.

Un type d'annonce décrivant la fonction

Type Tsumatotal \u003d Fonction (facteur: Integer): Entier;

Chargement de la bibliothèque

Dll_instance: \u003d LoadLibrary ("exemple_dll.dll");

Nous obtenons un pointeur sur la fonction

@Summatotal: \u003d getProcaddress (dll_instance, "sommatotal");

Fonction d'appel

Label1.Caption: \u003d inttostr (Summatotal (5));

Librarier de déchargement de la mémoire

Freelibrary (dll_instance);

Appel dynamique Dll Cela nécessite plus de travail, mais il est plus facile de gérer les ressources en mémoire. Si vous devez utiliser Dll Dans le programme, la charge statique est préférable. N'oubliez pas d'utiliser le bloc essayez ... sauf et essayez ... enfin.Pour éviter les erreurs lors de l'exécution du programme.

Chaîne d'exportation

Créé Dll En utilisant Delphi, peut également être utilisé dans des programmes écrits dans d'autres langages de programmation. Pour cette raison, nous ne pouvons utiliser aucun type de données. Les types existants dans Delphi peuvent être absents dans d'autres langues. Il est conseillé d'utiliser vos propres types de données à partir de Linux ou de Windows. Notre Dll Il peut être utilisé par un autre programmeur utilisant un autre langage de programmation.

Peut être utilisé cordes et tableaux dynamiques dans Dllécrit à Delphi, mais pour cela, vous devez connecter le module Shammem. Dans la section les usages. dans Dll et le programme qui l'utilisera. De plus, cette annonce doit être la première dans la section les usages. Chaque fichier de projet.

Les types chaîneComme nous le savons, C, C ++ et d'autres langues n'existent pas, il est donc souhaitable d'utiliser à la place d'eux Pchar..

Exemple dll

exemple de bibliothèque_dll; Utilise les systèmes, les classes; var (variables de déclaration) K1: Entier; K2: Entier; (DÉCLARE FONCTION) Fonction SUBSTATOAL (FACTEUR: Entier): Entier; S'inscrire; Commencer le facteur: \u003d facteur * k1 + facteur; Facteur: \u003d facteur * k2 + facteur; Résultat: \u003d facteur; finir; (Exporter une fonction pour une utilisation ultérieure par le programme) Exportations Sumatotal; (Initialisation des variables) commence K1: \u003d 2; K2: \u003d 5; finir.

Un exemple d'appel d'une fonction de la DLL

Unité unité1; L'interface utilise des fenêtres, des messages, des sysutils, des variantes, des classes, des graphiques, des commandes, des formulaires, des dialogues, des STDCTRLS; Type TForm1 \u003d Button de classe (Tform )1: Tbutton; Bouton de procédure1Cliquez sur (expéditeur: TOGIBY); Fin privé (déclarations publiques) fin; Type Tsummatotal \u003d Fonction (facteur: Integer): entier; VAR FORM1: TFORM1; Mise en œuvre ($ R * .DFM) Procédure Tform1.button1click (expéditeur: togject); var dll_instance: Thandle; Summatotal: Tsummatotal; commencer dll_instance: \u003d LoadLibrary ("exemple_dll.dll"); @Summatotal: \u003d getProcaddress (dll_instance, "sommatotal"); Label1.Caption: \u003d inttostr (Summatotal (5)); Freelibrary (dll_instance); finir; finir.

Comme vous le savez probablement, dans les bibliothèques de plug-in dynamiquement (DLL), les accords C sont utilisés lors de la déclaration d'objets exportés, tandis que C ++ utilise un système de génération de noms légèrement différent lors de la compilation, il est donc impossible de simplement exporter des fonctions - C + + Méthodes de classe, puis utilisez-les dans le code de l'application client (ici et ensuite sous le client désigne une application à l'aide de la DLL). Cependant, cela peut être fait à l'aide d'interfaces disponibles et de la DLL et de l'application cliente. Cette méthode est très puissante et à la fois élégante, car Le client ne voit qu'une interface abstraite et la classe actuelle qui implémente toutes les fonctions peut être n'importe laquelle. Microsoft "Modèle d'objet complexe) est construit sur une idée similaire (plus une fonctionnalité supplémentaire, bien sûr). Cet article expliquera comment utiliser l'approche" Classe "à l'aide d'une interface similaire à celle du com, avec un début (au stade de la compilation) et Tard (pendant le programme) contraignant.

Si vous avez déjà travaillé avec DLL, vous savez déjà que la DLL a une fonction spéciale DLLMain (). Cette fonctionnalité est similaire à WinMain, ou principale () en ce sens qu'il s'agit d'une sorte de point d'entrée d'aneth. Le système d'exploitation provoque automatiquement cette fonctionnalité dans le cas si la DLL est chargée et déchargée. Habituellement, cette fonction n'est utilisée pour aucun autre.

Il existe deux méthodes de connexion DLL au projet - il s'agit d'un début (au stade de la compilation du programme) et de la liaison ultérieure (pendant le programme). Les méthodes diffèrent dans la méthode de démarrage DLL et un moyen d'appeler des fonctions mises en œuvre et exportées à partir de la DLL.

Reliure précoce (pendant la compilation du programme)

Avec cette méthode de liaison, le système d'exploitation charge automatiquement la DLL pendant le démarrage du programme. Cependant, il est nécessaire que le projet soit activé. LIB Fichier (fichier de bibliothèque) correspondant à la DLL. Ce fichier détermine tous les objets DLL exportés. Une publicité peut contenir des classes conventionnelles ou des classes. Tout ce que le client a besoin d'utiliser ce fichier.Lib et activez le fichier d'en-tête DLL - et le système d'exploitation chargez automatiquement cette DLL. Comme on peut le voir, cette méthode semble très facile à utiliser, car Tous transparents. Toutefois, vous avez remarqué que le code client doit être recompilé chaque fois que le code DLL change et, en conséquence, est généré par un nouveau fichier One.lib. Est-ce pratique pour votre application - de vous résoudre. La DLL peut déclarer les fonctions qu'elle souhaite exporter, deux méthodes. Méthode standard - Utilisez des fichiers .def. Un fichier tel.def n'est que la liste des fonctions exportées à partir de la DLL.

// \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d // Bibliothèque de fichiers .def myFirstdll.dll Description "My First DLL" Exporte la myFunction // \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d // En-tête DLL, qui sera inclus dans le code client BOOL MYLL (INT PARMS); // \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d // Mise en œuvre d'une fonction dans la mycée DLL BOOL (INT PARMS) (// Faites tout ce dont vous avez besoin ............)

Je pense que nous ne pouvons pas dire que dans cet exemple, une seule fonction mycée est exportée. La deuxième méthode de déclarer des objets exportés est spécifique, mais beaucoup plus puissante: vous pouvez exporter non seulement des fonctions, mais également des classes et des variables. Regardons le fragment de code généré lors de la création d'une DLL appwizard "Ohms VisualC ++. Les commentaires inclus dans la liste suffisent pour comprendre comment tout fonctionne.

// \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d // Titre DLL, qui doit être activé dans le code client / * Le bloc IFDEF suivant est la méthode macro standard, qui donne des exportations à partir de la DLL plus facile. Tous les fichiers de cette DLL sont compilés avec une clé spécifique myFirstdll_exports. Cette clé n'est définie pour aucun des projets à l'aide de cette DLL. Ainsi, tout projet dans lequel ce fichier est activé, voit les fonctions MyFirstdll_API comme importées à partir de la DLL, tandis que la DLL lui-même voit les mêmes fonctions que les exportées. * / #IFDEF MYFIRSTDLL_EXPORTS #define MYFIRSTDLL_API __declspec (dllexport) #else #define MYFIRSTDLL_API __declspec (dllimport) #endif // La classe est exportée à partir test2.dll classe MYFIRSTDLL_API CMyFirstDll (public: CMyFirstDll (void); // TODO: vous peut ajouter vos méthodes.); extern myfirstdll_api int nmyfirstdll; Myfirstdll_api int fnmyfunction (nul);

Pendant la compilation DLL, la clé MyFirstdll_Exports est définie, le mot clé __DECLSPEC est donc défini avant les déclarations d'objets exportés (Dllexport). Et lorsque le code client est compilé, cette clé est incertaine et le préfixe O__DECLSPEC apparaît avant d'objets, de sorte que le client sache quels objets sont importés à partir de la DLL.

Dans les deux cas, tout ce dont vous avez besoin pour créer un client - ajoutez un fichier myfirstdll.lib au projet et activez le fichier d'en-tête qui annonce importé d'objets DLL, puis utilisez ces objets (fonctions, classes et variables) de la même manière que S'ils ont été identifiés et mis en œuvre localement dans le projet. Et maintenant, comprenons une autre méthode d'utilisation de la DLL, ce qui est plus souvent plus pratique et plus puissant.

Reliure ultérieure (pendant le programme)

Lorsque la liaison ultérieure est utilisée, la DLL n'est pas automatiquement chargée, lorsque le programme est démarré et directement dans le code, où il est nécessaire. Pas besoin d'utiliser des fichiers er.lib, l'application client ne nécessite donc pas de recompilement lors de la modification de la DLL. Une telle liaison a des fonctionnalités puissantes précisément parce que vous décidez quand et quel téléchargement de la DLL. Par exemple, vous écrivez le jeu dans lequel DirectX et OpenGL sont utilisés. Vous pouvez simplement activer tout le code nécessaire dans le fichier exécutable, mais pour déterminer que quelque chose sera tout simplement impossible. Ou vous pouvez mettre du code DirectX dans une DLL et le code OpenGL est à un autre et les connecter statilement au projet. Mais maintenant, l'ensemble du code est mutuellement dépendant, donc si vous avez écrit une nouvelle DLL contenant le code DirectX, le fichier exécutable devra recompiler. La seule commodité serait que vous n'avez pas besoin de prendre soin du téléchargement (bien que ce soit inconnu, que ce soit commodité si vous téléchargez les deux dlls, occupant la mémoire et en réalité, une seule n'est nécessaire). Et enfin, à mon avis, la meilleure idée est de permettre au fichier exécutable de choisir le téléchargement de la DLL lors du démarrage. Par exemple, si le programme a déterminé que le système ne prend pas en charge l'accélération OpenGL, il est préférable de télécharger la DLL avec le code DirectX, sinon vous téléchargez OpenGL. Par conséquent, la liaison enregistre la mémoire et réduit la relation entre la DLL et le fichier exécutable. Cependant, dans ce cas, la limite d'objets exportés est superposée - seule la fonction de style C peut être exportée. Les classes et les variables ne peuvent pas être téléchargées si le programme utilise une liaison ultérieure. Voyons comment se déplacer cette restriction à l'aide d'interfaces.

La DLL, conçue pour une liaison tardif utilise généralement le fichier .def pour déterminer ces objets qu'elle souhaite exporter. Si vous ne souhaitez pas utiliser de fichier.def, vous pouvez simplement utiliser le préfixe __declspec (Dllexport) devant les fonctions exportées. Les deux méthodes font la même chose. Le client charge la DLL en passant le nom du fichier DLL à la fonction Win32 LoadLibrary (). Cette fonction renvoie la poignée de hinstance utilisée pour fonctionner avec la DLL et qui est nécessaire pour décharger la DLL de la mémoire lorsqu'il ne devient pas nécessaire. Après avoir téléchargé la DLL, le client peut obtenir un pointeur à n'importe quelle fonction à l'aide de la fonction GetProcAddress () à l'aide du nom de la fonction souhaitée sous forme de paramètre.

// \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d // Bibliothèque de fichiers .def myFirstdll.dll Description "My First DLL" Exporte la myFunction // \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d / * Implémentation de la fonction dans DLL * / Bool MyFunction (Int PARMS) (// Faire quelque chose ................) // \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d // Code du client / * L'annonce de la fonction en réalité n'est nécessaire que pour déterminer les paramètres. Les annonces de fonctions sont généralement contenues dans le fichier d'en-tête fourni avec DLL. Le mot-clé extérieur C dans la déclaration de la fonction informe le compilateur que vous devez utiliser C. * / extern "C" variable mycée BOOL (INT PARMS) Typedef Bool (* MyFunction) (INT PARMS); Myfunction pfnmyfunc \u003d 0; // pointeur sur myfunction hinstance hmydll \u003d :: loadlibrary ("myfirstdll.dll"); Si (hmydll! \u003d null) (// Déterminez l'adresse de la fonction pfnmyfunc \u003d (mycée) :: getProcAddress (hmydll, "myfunction"); // en cas de non retenue - DLL de déchargement si (pfnmyfunc \u003d\u003d 0) (:: freelibrary (Hmydll); retour;) // appelez le résultat BOOL \u003d PFNMYFunc (PARMS) Fonction; // Décharger DLL si nous n'avons pas besoin de plus :: freelibrary (hmydll);)

Comme vous pouvez le constater, le code est assez simple. Et voyons maintenant comment travailler avec des "classes" peut être mis en œuvre. Comme il a été indiqué plus tôt, s'il est utilisé une liaison ultérieure, il n'existe aucune méthode directe à l'importation de classes DLL. Nous devons donc mettre en œuvre la "fonctionnalité" de la classe à l'aide d'une interface contenant toutes les fonctions ouvertes (publiques), à l'exclusion du concepteur. et destructeur. L'interface sera la structure C / C ++ habituelle contenant uniquement des fonctions de membre abstrait virtuel. La classe actuelle de la DLL sera héritée de cette structure et mettra en œuvre toutes les fonctions définies dans l'interface. Maintenant, afin d'accéder à cette classe à partir de l'application client, tout ce que vous devez faire est d'exporter des fonctions de style C correspondant à l'instance de classe et de les associer à l'interface définie par nous afin que le client puisse les utiliser. Pour mettre en œuvre cette méthode, deux autres fonctions sont nécessaires, dont l'une d'entre elles créera une interface et la seconde supprimera l'interface après la fin de celle-ci. Un exemple de mise en œuvre de cette idée est indiqué ci-dessous.

// \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d // Bibliothèque de fichiers .def myInterface.dll Description "Implémente l'I_MYINTORFACE Exports getmyinterface freemyInterface // \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d \u003d \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d // Titon Fal, utilisé dans la DLL et client, // qui annonce inétrifiez // i_myinterface.h struct i_myinterface (Virtual Bool Init (int Parms) \u003d 0; Libération Virtual Bool () \u003d 0; Dostuff Virtuel VID (); / * Annonces de DLL exportée Fonctions et définitions des types de pointeur sur les fonctions pour un téléchargement facile et des opérations avec des fonctions. Notez le préfixe externe "C", qui indique au compilateur que la fonction C-style * / externe "C" est utilisée (HRESULT GetMyInterface (I_MYINTORFACE ** PINTERFACE ); Typedef HRESULT (* getterface) (i_myinterface ** PINTERFACE); HRESULT FreemOnterface (I_MYINTORFACE ** PINTERFACE); TYPEDF HRESULT (* freeterface) (i_myinterface ** PInterface);) // \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d \u003d\u003d \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d // Mise en œuvre d'une interface dans dll // myInterface.h classe cmyclass: public i_myinterface (public: BOOL INIT (INT PARMS); libération de bool (); Noid Dostuff (); Cmyclass (); ~ Cmyclass (); // Tout autre membre de la classe ............ Privé: // Tout membre de la classe ............); // \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d // Les fonctions exportées qui créent et détruisent l'interface // dllmain.h hresult getmyinterface (i_myinterface ** pINterface) (si (! * PINTERFACE) (* PINTERFACE \u003d nouveau cmyclass; retour S_OK;) retour e_fail;) hresult freemyinterface (i_myinterface ** pINterface) (si (! * PInterface) renvoie e_fail; Supprimer * PINTERFACE; * PINTERFACE \u003d 0; retournez S_OK;) // \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d \u003d // code client // des annonces d'interface et appelez les fonctions getterface pfninterface \u003d 0; // pointeur sur la fonction getmyinterface i_myminterface * pINTERFACE \u003d 0; // pointeur sur la structure de myInterface hinstance hmydll \u003d :: LoadLibrary ("myInterface.dll "); Si (hmydll! \u003d null) (// Déterminez l'adresse de la fonction pfninterface \u003d (getterface) :: getProcAddress (hmydll, "getmyinterface"); // déchargez la DLL si l'opération précédente s'est terminée en cas de défaillance si (pfninterface \u003d\u003d 0) (:: freelibrary (hmydll); retour;) // Appelez la fonction HRESULT HR \u003d PFNInterface (& PINTERFACE); // Déchargez s'il est infructueux si (échec (HR)) (:: freelibrary (hmydll); retour;) // Interface chargée, vous pouvez appeler PINTERFACE-\u003e INIT (1); PINTERFACE-\u003e DOSTFF (); PIMINTFACE-\u003e Libération (); // Libérer l'interface Freeterface PFNFREE \u003d (HMYDLL, "FREEMYInterface"); Si (pfnfree! \u003d 0) pfnfree (& hmydll); // décharger DLL :: freelibrary (hmydll); )

Cette information suffit pour que vous sentiez toute la commodité d'utiliser des interfaces. Programmation réussie!

De la naissance (ou un peu plus tard), le système d'exploitation Windows a utilisé la bibliothèque dll dynamique (bibliothèque de liaison dynamique), qui contenait les réalisations des fonctions les plus fréquemment utilisées. Les héritiers de Windows - NT et Windows 95, ainsi que OS / 2 - dépendent également des bibliothèques DLL en termes d'une partie importante de leurs fonctionnalités.

Considérons un certain nombre d'aspects de la création et de l'utilisation de bibliothèques DLL:

  • comment connecter statiquement les bibliothèques DLL;
  • comment télécharger dynamiquement les bibliothèques DLL;
  • comment créer des bibliothèques DLL;
  • comment créer des extensions de DLL de bibliothèques MFC.

Utilisation de la DLL

Il est presque impossible de créer une application Windows dans laquelle les bibliothèques DLL ne seront pas utilisées. La DLL contient toutes les fonctions de l'API Win32 et du nombre indénombrable d'autres fonctions des systèmes d'exploitation WIN32.

De manière générale, la DLL n'est que des ensembles de fonctions collectées dans la bibliothèque. Cependant, contrairement à ses proches statiques (fichiers. Lib), les bibliothèques DLL ne sont pas attachées directement aux fichiers exécutables à l'aide de l'éditeur de liaison. Sur le fichier exécutable ne saisit que des informations sur leur emplacement. Au moment de l'exécution du programme, toute la bibliothèque est entièrement chargée. En raison de cela, différents processus peuvent utiliser ensemble seul et les mêmes bibliothèques de la mémoire. Cette approche vous permet de réduire la quantité de mémoire requise pour plusieurs applications en utilisant de nombreuses bibliothèques communes, ainsi que de contrôler les tailles d'ex-fichiers.

Toutefois, si la bibliothèque n'est utilisée que par une application, il est préférable de le rendre habituel, statique. Bien sûr, si les fonctions incluses dans sa composition ne seront utilisées que dans un programme, vous pouvez simplement insérer le fichier correspondant avec le texte source.

Le plus souvent, le projet est connecté à la DLL statiquement, ou implicitement, à la phase de mise en page. Téléchargement de la DLL lors de l'exécution du programme, contrôle le système d'exploitation. Cependant, la DLL peut être téléchargée et explicitement, ou de manière dynamique pendant l'application.

Importation des bibliothèques

Avec une connexion statique de la DLL, le fichier nom.lib est déterminé entre autres paramètres de l'éditeur de liaison de la ligne de commande ou dans l'onglet "Link" de la boîte de dialogue Paramètres du projet de l'environnement de studio de développeur. Toutefois. Fichier LIB utilisé une fois implicitement connectant la DLL, - Ce n'est pas une bibliothèque statique ordinaire. Les fichiers de tels.lib sont appelés bibliothèques d'importation (Bibliothèques d'importation). Ils ne contiennent pas le code de la bibliothèque lui-même, mais uniquement des références à toutes les fonctions exportées à partir du fichier DLL dans lequel tout est stocké. À la suite de la bibliothèque d'importation, en règle générale, ont une taille plus petite que les fichiers DLL. Nous reviendrons sur les moyens de les créer plus tard. Et considérons maintenant d'autres questions concernant la connexion implicite des bibliothèques dynamiques.

Interfaces de réconciliation

Lorsque vous utilisez vos propres bibliothèques ou bibliothèques de développeurs indépendants, vous devrez faire attention à la coordination de la fonction de la fonction avec son prototype.

Si le monde était parfait, les programmeurs n'auraient pas à s'inquiéter de l'harmonisation des interfaces de fonctions lorsque les bibliothèques sont connectées - elles seraient toutes les mêmes. Cependant, le monde est loin d'être excellent et de nombreux grands programmes sont écrits à l'aide de diverses bibliothèques sans C ++.

Par défaut, les fonctionnalités Visual C ++ sont cohérentes avec les règles C ++. Cela signifie que les paramètres sont entrés dans la pile à droite à gauche, le programme d'appel est responsable de la suppression de la pile lorsque vous quittez la fonction et développez son nom. Nom Mangling permet à l'éditeur de liaison de distinguer les fonctions surchargées, c'est-à-dire Fonctionne avec les mêmes noms, mais différentes listes d'arguments. Cependant, dans l'ancienne bibliothèque, il n'y a aucune fonction avec des noms avancés.

Bien que toutes les autres règles permettant d'appeler la fonction avec identique aux règles pour appeler une fonction en C ++, dans les bibliothèques avec des noms de fonctionnalités ne se développent pas. Cela ajoute seulement avant le soulignement (_) à venir.

Si vous devez connecter la bibliothèque avec l'application à C ++, toutes les fonctions de cette bibliothèque devront déclarer comme au format externe C:

Externe "avec" int myoldcFunction (int myParam);

Les fonctionnalités de la bibliothèque sont généralement placées dans le fichier d'en-tête de cette bibliothèque, bien que la plupart des en-têtes de bibliothèques ne soient pas conçus pour une utilisation dans des projets C ++. Dans ce cas, vous devez créer une copie du fichier d'en-tête et inclure un modificateur "C" externe pour déclarer toutes les fonctions de la bibliothèque. Le modificateur externe "C" peut également être appliqué à l'ensemble du bloc à laquelle l'ancien-tête S. est connecté à l'aide de la directive #Trimlude, donc au lieu de la modification de chaque fonction séparément, vous pouvez faire en seulement trois rangées:

Externe "c" (#include "myClib.h")

Dans les programmes pour les anciennes versions de Windows, les appels de langues Pascal pour les fonctions de l'API Windows ont également été utilisés. Dans les nouveaux programmes, utilisez le modificateur Winapi converti en _stDCall. Bien que ce ne soit pas une interface de fonctionnalité standard avec ou C ++, mais c'est qu'il est utilisé pour faire appel aux fonctions de l'API Windows. Cependant, généralement tout cela est déjà pris en compte dans les en-têtes Windows standard.

Chargement de la DLL implicitement branchée

Lors du démarrage, l'application essaie de trouver tous les fichiers DLL implicitement connectés à l'application et placez-les dans la zone RAM occupée par ce processus. Les fichiers de recherche Le système d'exploitation DLL est effectué dans la séquence suivante.

  • Le répertoire dans lequel le fichier EX est situé.
  • Catalogue de processus actuel.
  • Répertoire système Windows.

Si la bibliothèque DLL n'est pas détectée, l'application affiche une boîte de dialogue avec un message sur son absence et ses chemins pour lesquels la recherche a été effectuée. Ensuite, le processus est éteint.

Si la bibliothèque souhaitée est trouvée, elle est placée dans la mémoire opérationnelle du processus, où elle reste jusqu'à sa fin. Maintenant, l'application peut accéder aux fonctions contenues dans la DLL.

DLL de chargement et de déchargement dynamique

Au lieu de Windows pour effectuer une liaison dynamique de la DLL lorsque l'application est téléchargée pour la première fois en RAM, vous pouvez associer le programme avec le module de bibliothèque lors de l'exécution du programme (avec cette méthode, au cours de l'application, vous n'avez pas besoin d'utiliser la bibliothèque d'importation. ). En particulier, vous pouvez déterminer quelle bibliothèque DLL est disponible pour l'utilisateur ou permettre à l'utilisateur de choisir quelle bibliothèque sera chargée. Ainsi, vous pouvez utiliser différentes DLL dans lesquelles les mêmes fonctions effectuent diverses actions. Par exemple, une application destinée à une transmission de données indépendante sera en mesure de créer une solution pendant l'exécution, que la DLL soit chargée pour le protocole TCP / IP ou pour un autre protocole.

Télécharger une DLL régulière

La première chose à faire lorsque vous devez télécharger dynamiquement la DLL consiste à placer le module de bibliothèque dans la mémoire de processus. Cette opération est effectuée à l'aide de la fonction. :: LoadLibrary.avoir un seul argument - le nom du module chargé. Le fragment correspondant du programme devrait ressembler à ceci:

Hinstance hmydll; :: Si (((hmydll \u003d :: Loadlibrary ("mydll)) \u003d\u003d null) (/ * Impossible de télécharger la DLL * /) Sinon (/ * L'application a la droite pour utiliser les fonctions DLL via hmydll * /)

L'extension de fichier de bibliothèque Windows standard croit que si vous ne spécifiez pas une autre extension. Si le nom du fichier est répertorié et que le chemin est indiqué, il sera utilisé uniquement pour rechercher un fichier. Sinon, Windows recherchera le fichier dans le même schéma que dans le cas des DLL connectées implicitement, à partir du répertoire à partir duquel le fichier EXE est chargé et continuant en fonction de la valeur du chemin.

Lorsque Windows détecte un fichier, son chemin complet sera comparé aux bibliothèques DLL déjà chargées par ce processus. Si l'identité est trouvée, au lieu de télécharger une copie de l'application, une bibliothèque déjà connectée déjà connectée est renvoyée.

Si le fichier est détecté et que la bibliothèque a été chargée avec succès, fonctionne :: LoadLibrary.renvoie son descripteur, utilisé pour accéder aux fonctions de la bibliothèque.

Avant d'utiliser les fonctions de la bibliothèque, vous devez obtenir leur adresse. Pour ce faire, profitez d'abord de la directive typedef. Pour déterminer le type de pointeur sur la fonction et déterminer la variable de ce nouveau type, par exemple:

// type pfn_myfunction déclarera un pointeur à une fonction, // recevant un pointeur sur un tampon de caractères et une valeur exceptionnelle de l'INT TypeDef int (winapi * pfn_myfunction) (char *); :: pfn_myfunction pfnmyfunction;

Ensuite, vous devez obtenir un descripteur de bibliothèque, en utilisant et définir des adresses de fonctions, telles qu'une adresse de fonction nommée mycée:

Hmydll \u003d :: loadlibrary ("mydll"); Pfnmyfunction \u003d (pfn_myfunction) :: getProcaddress (hmydll, "myfunction"); :: int icode \u003d (* pfnmyfunction) ("Bonjour");

L'adresse de la fonction est déterminée par fonction :: getProcaddressElle devrait envoyer le nom de la bibliothèque et le nom de la fonction. Ce dernier doit être transmis sous la forme dans laquelle est exporté de la DLL.

Vous pouvez également vous reporter à la fonction par numéro de séquence par lequel il est exporté (en même temps, un fichier DEF doit être utilisé pour créer la bibliothèque, il sera décrit plus loin):

Pfnmyfunction \u003d (pfn_myfunction) :: getProcaddress (hmydll, maketitiservace (1));

Une fois rempli de fonctionner avec une bibliothèque de présentation dynamique, il peut être déchargé à partir de la mémoire de la mémoire à l'aide de la fonction :: Preelibrary.:

:: freelibrary (hmydll);

Chargement des extensions MFC de bibliothèques dynamiques

Lorsque vous téléchargez des extensions MFC pour la DLL (en détail sur le détail est décrit ci-dessous) au lieu de fonctions LoadLibrary.et Preelibrary.les fonctions sont utilisées AfxLoadLibrary. et Afxfreeelibrary. Ces derniers sont presque identiques aux fonctions de l'API Win32. Ils garantissent en outre que les structures MFC initialisées par l'extension DLL n'étaient pas axées sur d'autres threads.

Ressources DLL

Le téléchargement dynamique est applicable aux ressources DLL utilisées par MFC pour télécharger des ressources d'application standard. Pour ce faire, vous devez d'abord appeler la fonction. LoadLibrary.et placez la DLL en mémoire. Puis en utilisant la fonction AfxSetResourceHandle Vous devez préparer la fenêtre du programme pour recevoir des ressources de la bibliothèque nouvellement téléchargée. Sinon, les ressources seront chargées à partir de fichiers connectés au fichier de processus étant exécutés. Cette approche est pratique si vous devez utiliser différents ensembles de ressources, par exemple pour différentes langues.

Commenter. En utilisant une fonction LoadLibrary. Vous pouvez également télécharger des fichiers exécutables dans la mémoire (ne les exécutez pas pour l'exécution!). Le descripteur du module peut ensuite être utilisé lors de l'accès aux fonctions. Findresource.et Loadresource. Rechercher et télécharger des ressources d'application. Décharger des modules de la mémoire à l'aide d'une fonction Preelibrary..

Un exemple de DLL ordinaire et de méthodes de démarrage

Nous donnons le code source d'une bibliothèque connectée dynamiquement appelée mydll et contient une fonction de mycéens qui affiche simplement un message.

Premièrement, le fichier d'en-tête est déterminé par macrocontStant Exportation. Utilisation de ce mot-clé Lorsque vous déterminez une fonction de la fonction, une bibliothèque connectée dynamiquement vous permet d'informer la liaison que cette fonctionnalité est disponible pour une utilisation par d'autres programmes, à la suite de laquelle elle l'entre dans la bibliothèque d'importation. De plus, une telle fonction, comme la procédure de la fenêtre, doit être déterminée à l'aide d'une constante Rappeler:

Mydll.h. #Define export externe "c" __declspec (dllexport) exportation int rappelle mycée (Char * STR);

Le fichier de bibliothèque est également quelque peu différent des fichiers réguliers en C pour Windows. En elle au lieu de fonction Winmain. Il y a une fonction Dllmain.. Cette fonctionnalité est utilisée pour exécuter l'initialisation, qui sera décrite plus tard. Pour que la bibliothèque reste une fois qu'elle est chargée dans la mémoire et qu'il a été possible d'appeler des fonctions informatiques, il est nécessaire que sa valeur de retour soit vraie:

Mydll.c #include. #include "mydll.h" int winpi dllmain (hinstance hinstance, dword fdrason, pvoïde pvreserved) (retour true;) exporter int rappelle myfunction (Char * str) (MessageBox (NULL, STR, "FONCTION DE DLL", MB_OK ); Retour 1;)

Après diffusion et pose ces fichiers, deux fichiers apparaissent - mydll.dll (bibliothèque connectée dynamiquement elle-même) et mydll.lib (sa bibliothèque d'importation).

Un exemple de connexion implicite de l'application DLL

Donnons maintenant le code source d'une application simple qui utilise la fonction de mystage de la bibliothèque mydll.dll:

#Inclure. #include "mydll.h" int winpi winmain (Hinstance Hinstance, Hinstance HPRevinstance, LPSTR LPCMDLINE, INT NCMDShow) (int Icode \u003d MyFunction ("Bonjour"); retour 0;)

Ce programme ressemble à des programmes Windows ordinaires que ce qu'il est essentiellement. Toutefois, il convient de noter que dans le texte d'origine en plus d'appeler la fonction myfincune de la bibliothèque DLL, le fichier d'en-tête de cette bibliothèque myDLL.H est activé. Vous devez également vous connecter à la disposition de la mise en page. bibliothèque d'importation Mydll.lib (processus de connexion DLL implicite au module exécutable).

Il est extrêmement important de comprendre que le code de la fonction mysculose lui-même n'est pas inclus dans le fichier myapp.exe. Au lieu de cela, il existe simplement un lien vers le fichier mydll.dll et le lien vers la fonction MyFonction, qui se trouve dans ce fichier. Myapp.exe Fichier nécessite le début du fichier mydll.dll.

Le fichier d'en-tête mydll.h est inclus dans le fichier avec le texte source du programme myApp.ca comme le fichier Windows.h est allumé là-bas. Activation de la même manière que la bibliothèque d'importation MyDLL.LIB pour la mise en place de toutes les bibliothèques d'importation Windows. Lorsque MyApp.exe fonctionne, il se connecte à la bibliothèque mydll.dll exactement sur toutes les bibliothèques Windows connectées de manière dynamique standard.

Exemple de téléchargement dynamique application dll

Donnons maintenant un code complètement source d'une application simple qui utilise la fonction mysculose de la bibliothèque mydll.dll à l'aide du chargement dynamique de la bibliothèque:

#Inclure. Typedef int (Winapi * pfn_myfunction) (Char *); int Winapi Winmain (Hinstance Hinstance, Hinstance HPRevinstance, LPSTR LPCMDLINE, INT NCMDSHOWS) (HMYDLL \u003d (((((HMYDLL \u003d LoadLibrary ("mydll)) \u003d\u003d null) retour 1; pfn_myfunction pfnmyfunction \u003d (pfn_myfunction) getProcAddress (hmydll , "Myfunction"); int iode \u003d (* pfnmyfunction) ("Bonjour"); freeelibrary (hmydll); retour 0;)

Créer une dll

Maintenant, après avoir connu les principes des bibliothèques DLL dans les applications, examinez les moyens de les créer. Lors de l'élaboration d'une application de la fonction auquel plusieurs processus sont dessinés, il est souhaitable de poster dans la DLL. Cela vous permet d'utiliser plus rationnellement la mémoire dans Windows.

Le moyen le plus simple de créer un nouveau projet DLL à l'aide de l'Assistant AppWizard, qui effectue automatiquement de nombreuses opérations. Pour les DLL simples, telles que celles prises en compte dans ce chapitre, vous devez sélectionner le type de projet de bibliothèque Win32 dynamic-link. Le nouveau projet sera attribué à tous les paramètres nécessaires pour créer une bibliothèque DLL. Les fichiers de textes de source devront ajouter manuellement au projet.

S'il est prévu d'utiliser pleinement la fonctionnalité MFC, telle que des documents et des vues ou l'intention de créer un serveur OLE Automation, il est préférable de choisir le type de projet MFC AppWizard (DLL). Dans ce cas, en plus d'attribuer le projet aux paramètres de la connexion des bibliothèques dynamiques, le maître fera des travaux supplémentaires. Le projet ajoutera les liens nécessaires aux bibliothèques MFC et aux fichiers source contenant la description et la mise en œuvre de l'objet Classe d'objet dans la bibliothèque DLL dérivée de la bibliothèque DLL. Cwinapp..

Parfois, il est pratique de créer d'abord un type de projet MFC Appwizard (DLL) comme application de test, puis - la bibliothèque DLL comme composant. À la suite de la DLL, si nécessaire, sera créée automatiquement.

Fonction dllmain

La plupart des bibliothèques DLL sont simplement une collection de fonctions pratiquement indépendantes de l'autre exportées aux applications et utilisées dans elles. En plus des fonctions destinées à l'exportation, dans chaque bibliothèque DLL, il existe une fonction Dllmain.. Cette fonctionnalité est conçue pour initialiser et nettoyer la DLL. Elle est venue pour remplacer les fonctions Libmain. et Wep.Appliqué dans les versions précédentes de Windows. Structure de la fonction la plus simple Dllmain. Peut regarder, par exemple, donc:

Bool Winapi Dllmain (poignée Hinst, Dword dword, LPVoid \u003d true; commutateur (DWR SWITH) (DLR SWASTACH: // Initialisation du processus. Case DLL_THRead_ATTACH: // Initialisation du débit. Case DLL_THRead_Detach: // Structures de flux de nettoyage. Break; CAS DLL_PROCESS_DETACH: // Nettoyage des structures de processus. Pause;) Si (Ballwentwell) retourne vrai; sinon retourne faux;)

Une fonction Dllmain.appelé dans plusieurs cas. La cause de son appel est déterminée par le paramètre dwreason, qui peut prendre l'une des valeurs suivantes.

Lorsque la bibliothèque DLL est chargée pour la première fois, la fonction est appelée fonction. Dllmain. Avec dwrason, égal à dll_process_attach. Chaque fois que vous créez un nouveau ruisseau DLLMaino avec une DWR SWReAver, égal à DLL_THRead_ATTACH (à l'exception du premier fil, car dans ce cas, Dwwrason est égal à dll_process_attach).

À la fin du processus, fonctionnez avec la fonction DLL Dllmain. Appelé avec le paramètre dwrason égal à dll_process_detach. Lors de la destruction du ruisseau (sauf le premier) dwrève sera égal à dll_thread_detach.

Toutes les opérations sur l'initialisation et le nettoyage des processus et des flux dans lesquels la DLL a besoin, doit être effectuée sur la base de la valeur DWRASE, comme indiqué dans l'exemple précédent. L'initialisation des processus est généralement limitée à l'attribution de ressources partagées par les flux, en particulier de téléchargement de fichiers partagés et d'initialisation de la bibliothèque. L'initialisation des flux est utilisée pour configurer les modes caractéristiques que par ce fil, par exemple, pour initialiser la mémoire locale.

La DLL peut inclure des ressources qui n'appartiennent pas à l'application qui appelle cette bibliothèque. Si la DLL fonctionne avec des ressources DLL, il serait évident qu'il est utile de sauvegarder le descripteur Hinst quelque part dans un endroit isolé et de l'utiliser lorsque vous téléchargez des ressources à partir de la DLL. Le pointeur ipresservé est réservé à l'utilisation interne des fenêtres. Par conséquent, la demande ne devrait pas le réclamer. Vous pouvez seulement vérifier sa valeur. Si la bibliothèque DLL a été chargée de manière dynamique, elle sera null. Avec chargement statique, ce pointeur sera non nul.

En cas d'achèvement réussi, la fonction Dllmain. Doit retourner vrai. En cas d'erreur, FALSE est renvoyé et d'autres actions sont terminées.

Commenter. Si vous n'écrivez pas votre propre fonction DLLMAIN (), le compilateur connectera la version standard qui retourne simplement true.

Fonctions exportatrices de la DLL

Pour appliquer l'application pour désigner les fonctions de la bibliothèque dynamique, chacune d'entre elles devrait occuper une chaîne dans le tableau des fonctions DLL exportées. Il existe deux façons d'adopter la fonction de ce tableau à la phase de compilation.

__DeclSpec (dllexport)

Vous pouvez exporter une fonction de la DLL en mettant le modificateur __DECLSPEC au début de sa description. De plus, MFC comprend plusieurs macros définissant __declspec (Dllexport), y compris AFX_CLASS_EXPORT, AFX_DATA_EXPORT et AFX_API_EXPORT.

La méthode __declSpec n'est pas aussi souvent qu'une deuxième méthode qui fonctionne avec les fichiers de définition de module (.def) et vous permet de mieux gérer le processus d'exportation.

Fichiers de définition de module

La syntaxe des fichiers avec une extension .def dans Visual C ++ est une ligne assez droite, principalement parce que les paramètres complexes utilisés dans les premières versions de Windows ne sont plus utilisés dans Win32. Comme il ressort clairement de l'exemple simple suivant, le fichier .def contient le nom et la description de la bibliothèque, ainsi qu'une liste des fonctions exportées:

Mydll.def. Bibliothèque "mydll" Description "MyDLL - Exemple de la bibliothèque DLL" Exporte des exportations MyFunction @ 1

Dans la ligne d'exportation, vous pouvez spécifier son numéro de séquence, mettre le symbole @ avant cela. Ce numéro sera ensuite utilisé lors de la mise en contact GetProcAddress (). En fait, le compilateur attribue des nombres ordinaux à tous les objets exportés. Cependant, la façon dont il fait cela est en partie imprévisible si vous ne cédez pas clairement ces chiffres.

Dans la ligne d'exportation, vous pouvez utiliser le paramètre non nom. Il interdit au compilateur d'inclure le nom de la fonction dans la table d'exportation DLL:

Myfunction @ 1 noname

Parfois, il vous permet d'économiser beaucoup d'espace dans le fichier DLL. Les applications utilisant la bibliothèque d'importation pour la connexion de la DLL implicite ne "remarqueront" "remarquer" des différences, car elles sont implicites implicitement, les numéros de séquence sont utilisés automatiquement. Applications téléchargeant des bibliothèques DLL doivent être transmises de manière dynamique à GetProcAddressle numéro de séquence, pas le nom de la fonction.

Lorsque vous utilisez le fichier def ci-dessus, la description des fonctions exportées de la bibliothèque DLL peut être, par exemple, ne: pas:

#Define export externe "c" __declspec (dllexport) exportation int rappelle mycée (Char * STR); A: extern "c" int Callback mysculction (Char * str);

Cours d'exportation

Créer un fichier .def pour exporter des classes encore simples de la bibliothèque dynamique peut être assez complexe. Il sera nécessaire d'exporter explicitement chaque fonction pouvant être utilisée par une application externe.

Si vous regardez le fichier de distribution de la mémoire implémenté dans la classe, cela peut noter certaines fonctions très inhabituelles. Il s'avère qu'il existe des constructeurs et destructeurs implicites, des fonctions déclarées dans les macros MFC, en particulier _declare_message_map, ainsi que des fonctions écrites par le programmeur.

Bien que vous puissiez exporter chacune de ces fonctions séparément, il existe une méthode plus simple. Si vous utilisez le macromodificateur AFX_CLASS_EXPORT dans la déclaration de classe, le compilateur s'occupe de l'exportation des fonctions nécessaires permettant à l'application d'utiliser la classe contenue dans la DLL.

Mémoire dll

Contrairement aux bibliothèques statiques, qui, qui font essentiellement partie du code de l'application, la bibliothèque de présentation dynamique dans des versions de 16 bits de Windows a travaillé avec une mémoire quelque peu différemment. Sous le contrôle de la victoire 16, la mémoire DLL a été placée à l'extérieur de l'espace d'adressage de la tâche. Placer des bibliothèques dynamiques dans la mémoire globale fournissait la possibilité de partager par leurs différentes tâches.

Dans Win32, la bibliothèque DLL est située dans la mémoire du processus de traitement. Chaque processus est fourni avec une copie séparée de la mémoire DLL "globale", qui est réinitialisée à chaque fois qu'un nouveau processus est chargé. Cela signifie que la bibliothèque dynamique ne peut pas être utilisée ensemble, dans la mémoire globale, comme dans WinL6.

Et pourtant, en effectuant un certain nombre de manipulations complexes sur le segment de données DLL, vous pouvez créer une zone commune de la mémoire pour tous les processus à l'aide de cette bibliothèque.

Supposons qu'il existe une gamme d'entiers, qui devraient être utilisés par tous les processus qui téléchargent cette DLL. Cela peut être programmé comme suit:

#Pragma data_seg (". Myseg") int Sharedlnts; // Autres variables de partage #pragma data_seg () #pragma Commentaire (Lib, "MSVCRT" "Section: .Myseg, RWS");

Toutes les variables déclarées entre #pragma data_seg () directives sont placées dans Segment.MySeg. #Pragma Commentaire () La directive n'est pas un commentaire ordinaire. Cela vous donne une indication de la bibliothèque du système d'exécution avec la marque d'une nouvelle section telle que la lecture, l'écriture et le partage.

Compilation complète de la DLL

Si le projet de bibliothèque dynamique est créé à l'aide du fichier APPWIZARD I.DEF modifié en conséquence, cela suffit. Si les fichiers de projet sont créés manuellement ou d'autres méthodes sans l'aide d'AppWizard, le paramètre Link / DLL doit être inclus dans la ligne de commande de l'éditeur. En conséquence, la bibliothèque DLL sera créée au lieu du fichier autonome exécuté.

Si le fichier V.def a une chaîne de LIBRRRR, spécifiez spécifié le paramètre / DLL dans la ligne de commande de l'éditeur de relation n'est pas nécessaire.

Le MFC fournit un certain nombre de modes spéciaux liés à l'utilisation de la bibliothèque de la bibliothèque Dynamic MFC. La section suivante est consacrée à ce problème.

DLL et MFC.

Le programmeur n'est pas obligé d'utiliser MFC lors de la création de bibliothèques dynamiques. Cependant, l'utilisation de MFC ouvre un certain nombre de caractéristiques très importantes.

Il existe deux niveaux d'utilisation de la structure MFC dans la DLL. Les premiers sont la bibliothèque dynamique habituelle basée sur le MFC, Mfc dll (DLL MFC régulier). Il peut utiliser MFC, mais ne peut pas transmettre des pointeurs sur des objets MFC entre la DLL et les applications. Le deuxième niveau est mis en œuvre dans extensions dynamiques MFC. (DLL d'extensions MFC). L'utilisation de ce type de bibliothèques dynamiques nécessite des efforts de réglage supplémentaires, mais vous permet d'échanger librement des pointeurs à des objets MFC entre la DLL et l'application.

Dll normal MFC

Les DLL MFC conventionnelles vous permettent d'appliquer MFC dans des bibliothèques dynamiques. Dans ce cas, les applications qui font appel à de telles bibliothèques ne doivent pas nécessairement être construites sur la base du MFC. Dans les DLL conventionnelles, vous pouvez utiliser MFC de quelque manière que ce soit, notamment en créant de nouvelles classes dans la DLL sur la base de classes MFC et de les exporter vers des applications.

Cependant, les DLL ordinaires ne peuvent pas communiquer avec des applications aux classes dérivées du MFC.

Si l'application doit être échangée avec des pointeurs DLL vers des classes MFC ou de leurs dérivés, vous devez utiliser l'extension DLL décrite dans la section suivante.

L'architecture des DLL ordinaires est conçue pour utiliser d'autres environnements de programmation tels que Visual Basic et Powerbuilder.

Lors de la création d'une bibliothèque DLL MFC régulière à l'aide de Appwizard, un nouveau type de projet est sélectionné. MFC Appwizard (DLL). Dans la première boîte de dialogue Assistant d'application, vous devez sélectionner l'un des modes des bibliothèques dynamiques classiques: "DLL régulière avec MFC lié statistiquement lié" ou "DLL régulière à l'aide de la DLL MFC partagé". Le premier fournit statique et la seconde est la connexion dynamique des bibliothèques MFC. Ensuite, le mode de connexion MFC à DLL peut être modifié à l'aide de la liste combinée de l'onglet Général de la boîte de dialogue Paramètres du projet.

Gestion du statut MFC

Chaque module de processus MFC contient des informations sur son état. Ainsi, les informations sur l'état de la DLL diffèrent des informations d'état qui ont provoqué sa demande. Par conséquent, toute fonction exportée de la bibliothèque, accès à laquelle procède directement à partir d'applications, doit signaler le MFC, quelles informations d'état à utiliser. Dans la DLL MFC habituelle à l'aide des bibliothèques dynamiques MFC, avant d'appeler n'importe quel sous-programme MFC au début de la fonction exportée, vous devez placer la ligne suivante:

AFX_MANAGE_STATE (AFXGETSTATICTICMODULESTEATTE ());

Cet opérateur détermine l'utilisation des informations d'état pertinentes lors de l'exécution de la fonction appliquée à ce sous-programme.

Extensions dynamiques MFC.

MFC vous permet de créer des bibliothèques DLL perçues par les applications non définies de fonctions individuelles, mais comme des extensions MFC. Avec ce type de DLL, vous pouvez créer de nouvelles classes dérivées de classes MFC et utilisez-les dans leurs applications.

Pour fournir la possibilité de pointers d'échange gratuits aux objets MFC entre l'application et la DLL, vous devez créer une extension dynamique de MFC. DLL de ce type est connecté aux bibliothèques MFC dynamiques ainsi que toutes les applications utilisant l'extension dynamique MFC.

Pour créer une nouvelle extension dynamique MFC, le moyen le plus simple, à l'aide du maître d'application, attribuez un type de projet MFC Appwizard (DLL) Et à l'étape 1 incluent le mode "DLL d'extension MFC". En conséquence, le nouveau projet sera attribué à tous les attributs nécessaires de l'expansion dynamique du MFC. De plus, une fonction sera créée Dllmain. Pour la DLL, effectuer un certain nombre d'opérations spécifiques sur l'initialisation de l'extension DLL. Il convient de noter que les bibliothèques dynamiques de ce type ne contiennent pas et ne doivent pas contenir d'objets dérivés de Cwinapp..

Initialisation des extensions dynamiques

Pour "adapter" dans la structure MFC, les extensions dynamiques MFC nécessitent une configuration initiale supplémentaire. Les opérations pertinentes sont effectuées par fonction. Dllmain.. Considérez un exemple de cette fonction créée par l'Assistant AppWizard.

Statique afx_extension_module myextdll \u003d (, null); externe "c" int Apticenty dllmain (hinstance hinstance, Dword dwwrasher, LPVoid ipresservé) (si (dwweon \u003d\u003d dll_process_attach) (tracé ("myext.dll initialisant \\ n!"); // initialisation à une fois d'une date d'extension AFXINITEXTENSULEMODULE (myextDLL , hinstance); // Insérer cette DLL dans la chaîne de ressources Nouveau cdynlinklibrary (myextDll);) sinon si (dwrève \u003d\u003d dll_process_detach) (tracé ("" myext.dll terminer! \\ n ");) Retournez 1; / / D'ACCORD)

La partie la plus importante de cette fonctionnalité est le défi AfxinitextryxensionModule.. Cette initialisation de la bibliothèque dynamique, ce qui lui permet de fonctionner correctement dans la structure MFC. Les arguments de cette fonction sont le descripteur de bibliothèque DLL et la structure AFX_Extension_Module, qui contient des informations sur la bibliothèque dynamique connectée au MFC, est la DLLMain.

Pas besoin d'initialiser explicitement la structure Afx_extension_Module. Cependant, il est nécessaire de l'annoncer. L'initialisation sera engagée dans le concepteur Cdynlinklibrary.. DLL doit créer une classe Cdynlinklibrary.. Son constructeur initialisera non seulement la structure AFX_Extension_Module, mais ajoutez également une nouvelle bibliothèque à la liste DLL avec laquelle MFC peut fonctionner.

Chargement des extensions MFC dynamiques

À partir de la version 4.0 MFC vous permet de télécharger et de décharger de manière dynamique la DLL, y compris l'expansion. Pour effectuer correctement ces opérations sur la DLL créée dans sa fonction Dllmain. Au moment de la déconnexion du processus, vous devez ajouter un appel AFXTERMEXTENSILEMODULE. La dernière fonction afx_extension_module est transmise sous forme de paramètre en tant que paramètre. Faire ce texte Dllmain. Vous devez ajouter les lignes suivantes.

Si (dweaison \u003d\u003d dll_process_detach) (AfxtermextensionModule (myextDll);)

De plus, il convient de rappeler que la nouvelle bibliothèque de DLL est une expansion dynamique et doit être chargée et déchargée de manière dynamique à l'aide de fonctions. AfxLoadLibrary. et Afxfreeelibrary,mais non LoadLibrary. et Preelibrary..

Fonctions exportatrices des extensions dynamiques

Considérez maintenant la manière dont les fonctions d'exportation et les classes de l'expansion dynamique sont exportées. Bien que l'ajout de tous les noms étendus au fichier DEF peut être manuellement, il est préférable d'utiliser des modificateurs pour les annonces de classes et de fonctions exportées, telles que AFX_EXT_CLASS et AFX_EXT_API, par exemple:

Classe AFX_EXT_CLASS CMLYCLASS: Public CObject (// Votre déclaration de classe) Void AFX_EXT_API myFunc ();

Il existe trois méthodes de chargement DLL:

a) implicite;

c) différé.

Considérons la charge de la DLL implicite. Pour créer une application conçue pour la charge de la DLL implicite, vous devez avoir:

Dossier activé de la bibliothèque avec descriptions d'objets utilisés à partir de la DLL (prototypes de fonctions, classes et types classifiés). Ce fichier est utilisé par le compilateur.

Fichier LIB avec une liste d'identifiants importés. Ce fichier doit être ajouté aux paramètres du projet (dans la liste des bibliothèques utilisées par la liaison).

La compilation de projet est effectuée de la manière habituelle. Utilisation de modules d'objet et de fichier LIB, ainsi que de prendre en compte les liens vers des identificateurs importés, l'éditeur de liaison (linker, lien) forme le module de chargement ex. Dans ce module, la liaison place également la section d'importation où les noms de tous les modules DLL nécessaires sont répertoriés. Pour chaque DLL dans la section d'importation, il est indiqué que des liens dans le code exécutable se trouvent sur les noms des fonctions et des variables. Ces informations utiliseront le chargeur de démarrage du système d'exploitation.

Que se passe-t-il à l'étape d'exécution de l'application client? Après avoir démarré l'ex-module, le chargeur de système d'exploitation effectue les opérations suivantes:

1. crée un espace d'adressage virtuel pour un nouveau module exécutable de processus et de projets.

2. Analyse la section d'importation en définissant tous les modules DLL nécessaires et les a également projetés pour traiter l'espace du processus.

La DLL peut importer des fonctions et des variables d'une autre DLL. Ainsi, elle peut avoir sa propre section d'importation pour laquelle la même action doit répéter. En conséquence, cela peut prendre une période assez longue sur l'initialisation du processus.

Après avoir afficé l'ex-module et tous les modules DLL dans l'espace d'adressage du processus, son flux primaire est prêt à être exécuté et que l'application commence à fonctionner.

Les inconvénients de la botte implicite sont la charge de la bibliothèque obligatoire, même si l'appel à ses fonctions ne se produira pas et, en conséquence, l'exigence requise pour la présence d'une bibliothèque pendant la mise en page.

Le chargement explicite de la DLL élimine les défauts mentionnés ci-dessus en raison de la complication du code. Le programmeur doit prendre en charge le téléchargement de la DLL et connecter les fonctions exportées. Mais la charge explicite vous permet de charger la DLL au besoin et permet au programme de traiter les situations découlant de l'absence de DLL.

En cas de chargement explicite, le processus de travail avec la DLL se produit en trois étapes:

1. Téléchargez la DLL à l'aide de la fonction LoadLibrary.(ou son analogue étendu Loadlibraryex).En cas de chargement réussi, la fonction renvoie le descripteur HMODULE HLIB, ce qui vous permet d'accéder davantage à cette DLL.

2. Fonction d'appel GetProcAddressobtenir des pointeurs sur les fonctions requises ou d'autres objets. Comme la première fonction de paramètre GetProcAddressobtient un descripteur HLIB comme deuxième paramètre des lignes avec le nom de la fonction importée. Ensuite, le pointeur résultant est utilisé par le client. Par exemple, s'il s'agit d'un pointeur sur une fonction, la fonction souhaitée est appelée.

3. Lorsque la bibliothèque dynamique téléchargée n'est plus nécessaire, il est recommandé de le libérer en appelant la fonction Preelibrary.La bibliothèque de libération ne signifie pas que le système d'exploitation le supprimera immédiatement de la mémoire. Le délai de déchargement est fourni dans le cas où la même DLL devra à nouveau avoir une sorte de processus. Mais s'il y a des problèmes de RAM, Windows supprime d'abord les bibliothèques libérées de la mémoire.

Considérez la charge DLL différée. DLL Télécharger différé (DLL Delay-Charger) est une DLL de liaison implicite, qui n'est pas chargée tant que le code fait appel à un identifiant exporté de celui-ci. De telles DLL peuvent être utiles dans les situations suivantes:

Si l'application utilise plusieurs DLL, son initialisation peut occuper un long temps requis par le chargeur pour la projection de toutes les DLL à l'espace d'adressage du processus. Les téléchargements différés DLL vous permettent de résoudre ce problème en distribuant le démarrage DLL lors de l'application.

Si l'application est conçue pour fonctionner dans différentes versions du système d'exploitation, une partie des fonctions peut apparaître uniquement dans des versions ultérieures du système d'exploitation et non être utilisée dans la version actuelle. Mais si le programme ne provoque pas de fonction spécifique, la DLL n'est pas nécessaire, et il peut continuer à fonctionner en toute sécurité. Lorsqu'il s'agit d'inexistant

les fonctions peuvent être émises à l'utilisateur avec l'avertissement approprié.

Pour implémenter la méthode de téléchargement différée, vous devez ajouter non seulement la bibliothèque d'importation MYLIB.LIB souhaitée, mais également la bibliothèque système des importations retardump.lib. De plus, il est nécessaire d'ajouter un drapeau / retardlault: myLib.dll vers les options de liaison.

Les paramètres énumérés provoquent une liaison pour effectuer les opérations suivantes:

Introduisez une fonctionnalité spéciale dans l'ex-module
Retardloadhelper;

Supprimer myLib.dll de la section d'importation exécutable du module exécutable de sorte que le chargeur de démarrage du système d'exploitation ne tente pas d'effectuer le chargement implicite de cette bibliothèque à la phase de chargement de l'application;

Ajouter à Ex-Fichier une nouvelle section d'importation sédimulée avec une liste des fonctions importées de MyLib.dll;

Convertir des appels de fonctionnalités de la DLL aux appels
Retardloadhelper.

Au stade d'exécution de l'application, la fonction d'appel de la DLL est mise en œuvre par référence à retardloadHelper. Cette fonctionnalité, utilisant les informations de la section d'importation différée, appelle d'abord le premier chargement de LoadLibrary, puis GetProcAddress. Après avoir reçu l'adresse de la fonction DLL, RetardLoadHelper le rend afin que, à l'avenir, cette fonction DLL soit appelée directement. Notez que chaque fonction de la DLL est configurée individuellement lorsqu'elle est appelée pour la première fois.

Il existe deux façons de télécharger DLL: avec une mise en page explicite et implicite.

Avec une mise en page implicite, la fonction de la DLL téléchargeable est ajoutée à la section d'importation du fichier d'appel. Lorsque vous démarrez un tel fichier, le démarreur du système d'exploitation analyse la section d'importation et connecte toutes les bibliothèques spécifiées. Compte tenu de sa simplicité, cette méthode est très populaire; Mais la simplicité - la simplicité et la disposition implicite sont inhérentes à certains inconvénients et restrictions:

toutes les DLL connectées sont toujours chargées, même si pendant toute la session du programme, le programme ne se fera jamais à aucun d'entre eux;

si au moins l'une des DLL requises est manquante (ou la DLL n'exporte pas d'au moins une fonction requise) - Chargement du fichier exécutable est interrompue par le message "La bibliothèque de liaison dynamique n'a pas pu être trouvée" (ou quelque chose comme ça) - même si L'absence de cette DLL n'est pas critique pour l'exécution du programme. Par exemple, un éditeur de texte pourrait bien fonctionner dans une configuration minimale - sans module d'impression, tables de retrait, graphiques, formules et autres composants secondaires, mais si ces DLL sont chargées avec une mise en page implicite - vous voulez ne pas vouloir, vous voulez doivent "les tirer".

la recherche de la DLL se produit dans l'ordre suivant: Dans le répertoire contenant le fichier d'appel; dans le catalogue actuel du processus; Dans le catalogue système% Windows% System%; principalement répertoire% Windows%; Dans les catalogues spécifiés dans la variable du chemin. Il est impossible de définir un autre chemin de recherche (ou plutôt - il est possible, mais il sera nécessaire de modifier le registre du système, et ces modifications auront un impact sur tous les processus effectués dans le système - c'est-à-dire pas bon).

Layout explicite élimine toutes ces lacunes - au prix de certaines complications du code. Le programmeur lui-même devra s'occuper de charger la DLL et de connecter les fonctions exportées (sans oublier le contrôle des erreurs, sinon, un jour, le boîtier mettra fin à la suspension du système). Mais la mise en page explicite vous permet de charger la DLL au besoin et donne à un programmeur la capacité de traiter indépendamment les situations avec le manque de DLL. Vous pouvez aller plus loin - ne pas spécifier que le nom DLL du programme est clairement et numériser un tel répertoire pour la disponibilité des bibliothèques dynamiques et connecter toute l'application trouvée. C'est ainsi que le mécanisme de support Plug-In'ov travaille dans le célèbre gestionnaire de fichiers (et non seulement).