DLL d’extension de MFC
Une DLL d’extension MFC est une DLL qui implémente généralement des classes réutilisables dérivées des classes Microsoft Foundation Class Library existantes.
Une DLL d’extension MFC présente les fonctionnalités et exigences suivantes :
L’exécutable client doit être une application MFC compilée avec
_AFXDLL
définie.Une DLL d’extension MFC peut également être utilisée par une DLL MFC standard qui est liée dynamiquement à MFC.
Les DLL d’extension MFC doivent être compilées avec
_AFXEXT
la définition. Cela force_AFXDLL
également à être défini et garantit que les déclarations appropriées sont extraites des fichiers d’en-tête MFC. Elle garantit également qu’elleAFX_EXT_CLASS
est définie comme__declspec(dllexport)
lors de la génération de la DLL, ce qui est nécessaire si vous utilisez cette macro pour déclarer les classes dans votre DLL d’extension MFC.Les DLL d’extension MFC ne doivent pas instancier une classe dérivée
CWinApp
, mais doivent s’appuyer sur l’application cliente (ou DLL) pour fournir cet objet.Toutefois, les DLL d’extension MFC doivent fournir une
DllMain
fonction et y effectuer toute initialisation nécessaire.
Les DLL d’extension sont créées à l’aide de la version de bibliothèque de liens dynamiques de MFC (également appelée version partagée de MFC). Seuls les exécutables MFC (applications ou DLL MFC standard) générés avec la version partagée de MFC peuvent utiliser une DLL d’extension MFC. L’application cliente et la DLL d’extension MFC doivent utiliser la même version de MFCx0.dll. Avec une DLL d’extension MFC, vous pouvez dériver de nouvelles classes personnalisées de MFC, puis proposer cette version étendue de MFC aux applications qui appellent votre DLL.
Les DLL d’extension peuvent également être utilisées pour transmettre des objets dérivés de MFC entre l’application et la DLL. Les fonctions membres associées à l’objet passé existent dans le module où l’objet a été créé. Étant donné que ces fonctions sont correctement exportées lors de l’utilisation de la version DLL partagée de MFC, vous pouvez transmettre librement des pointeurs d’objet MFC ou dérivés de MFC entre une application et les DLL d’extension MFC qu’il charge.
Une DLL d’extension MFC utilise une version partagée de MFC de la même façon qu’une application utilise la version DLL partagée de MFC, avec quelques considérations supplémentaires :
Il n’a pas d’objet
CWinApp
dérivé. Elle doit fonctionner avec l’objetCWinApp
dérivé de l’application cliente. Cela signifie que l’application cliente possède la pompe de message principale, la boucle inactive, et ainsi de suite.Il appelle
AfxInitExtensionModule
dans saDllMain
fonction. La valeur de retour de cette fonction doit être vérifiée. Si une valeur zéro est retournée parAfxInitExtensionModule
, retournez 0 à partir de votreDllMain
fonction.Il crée un objet CDynLinkLibrary pendant l’initialisation si la DLL d’extension MFC souhaite exporter des
CRuntimeClass
objets ou des ressources vers l’application.
Avant la version 4.0 de MFC, ce type de DLL a été appelé AFXDLL. AFXDLL fait référence au _AFXDLL
symbole de préprocesseur défini lors de la génération de la DLL.
Les bibliothèques d’importation pour la version partagée de MFC sont nommées conformément à la convention décrite dans les conventions d’affectation de noms pour les DLL MFC. Visual Studio fournit des versions prédéfinies des DLL MFC, ainsi qu’un certain nombre de DLL non MFC que vous pouvez utiliser et distribuer avec vos applications. Ceux-ci sont documentés dans Redist.txt, qui est installé dans le dossier Program Files\Microsoft Visual Studio.
Si vous exportez à l’aide d’un fichier .def, placez le code suivant au début et à la fin de votre fichier d’en-tête :
#undef AFX_DATA
#define AFX_DATA AFX_EXT_DATA
// <body of your header file>
#undef AFX_DATA
#define AFX_DATA
Ces quatre lignes garantissent que votre code est compilé correctement pour une DLL d’extension MFC. L’abandon de ces quatre lignes peut entraîner la compilation ou la liaison incorrecte de votre DLL.
Si vous devez passer un pointeur d’objet dérivé de MFC ou MFC vers ou à partir d’une DLL MFC, la DLL doit être une DLL d’extension MFC. Les fonctions membres associées à l’objet passé existent dans le module où l’objet a été créé. Étant donné que ces fonctions sont correctement exportées lors de l’utilisation de la version DLL partagée de MFC, vous pouvez transmettre librement des pointeurs d’objet MFC ou dérivés de MFC entre une application et les DLL d’extension MFC qu’il charge.
En raison des problèmes liés au nom C++ et à l’exportation, la liste d’exportation à partir d’une DLL d’extension MFC peut être différente entre les versions de débogage et de vente au détail des mêmes DLL et DLL pour différentes plateformes. Le MFCx0.dll de vente au détail a environ 2 000 points d’entrée exportés ; le débogage MFCx0D.dll a environ 3 000 points d’entrée exportés.
Gestion de la mémoire
MFCx0.dll et toutes les DLL d’extension MFC chargées dans l’espace d’adressage d’une application cliente utilisent le même allocateur de mémoire, le chargement des ressources et d’autres états globaux MFC comme s’ils se trouvaient dans la même application. Cela est important, car les bibliothèques DLL non-MFC et les DLL MFC standard font exactement l’inverse et ont chaque DLL allouant hors de son propre pool de mémoire.
Si une DLL d’extension MFC alloue de la mémoire, cette mémoire peut librement être mélangée avec n’importe quel autre objet alloué à l’application. En outre, si une application qui lie dynamiquement à MFC échoue, la protection du système d’exploitation conserve l’intégrité de toute autre application MFC partageant la DLL.
De même, d’autres états MFC globaux, comme le fichier exécutable actuel à partir duquel charger des ressources, sont également partagés entre l’application cliente et toutes les DLL d’extension MFC, ainsi que MFCx0.dll elle-même.
Partage de ressources et de classes
L’exportation de ressources s’effectue via une liste de ressources. Chaque application contient une liste liée de manièreing des objets CDynLinkLibrary . Lorsque vous recherchez une ressource, la plupart des implémentations MFC standard qui chargent les ressources examinent d’abord le module de ressource actuel (AfxGetResourceHandle
) et si la ressource n’est pas trouvée, parcourez la liste des objets CDynLinkLibrary qui tentent de charger la ressource demandée.
La marche à pied de la liste présente les inconvénients qu’elle est légèrement plus lente et nécessite la gestion des plages d’ID de ressource. Il présente l’avantage qu’une application cliente qui lie plusieurs DLL d’extension MFC peut utiliser n’importe quelle ressource fournie par DLL sans avoir à spécifier le handle d’instance DLL. AfxFindResourceHandle
est une API utilisée pour parcourir la liste des ressources afin de rechercher une correspondance donnée. Il prend le nom et le type d’une ressource et retourne le handle de ressource où il a été trouvé (ou NULL).
Si vous ne souhaitez pas parcourir la liste et charger uniquement des ressources à partir d’un emplacement spécifique, utilisez les fonctions AfxGetResourceHandle
et AfxSetResourceHandle
enregistrez l’ancien handle et définissez le nouveau handle. Veillez à restaurer l’ancien handle de ressource avant de revenir à l’application cliente. Pour obtenir un exemple d’utilisation de cette approche pour charger explicitement un menu, consultez testdll2 .cpp dans l’exemple DLLHUSK MFC.
La création dynamique d’objets MFC en fonction d’un nom MFC est similaire. Le mécanisme de désérialisation des CRuntimeClass
objets MFC doit avoir tous les objets inscrits afin qu’il puisse reconstruire en créant dynamiquement des objets C++ du type requis en fonction de ce qui a été stocké précédemment.
Dans le cas de l’exemple DLLHUSK MFC, la liste ressemble à ceci :
head -> DLLHUSK.EXE - or - DLLHUSK.EXE
| |
TESTDLL2.DLL TESTDLL2.DLL
| |
TESTDLL1.DLL TESTDLL1.DLL
| |
MFCOxxD.DLL |
| |
MFCDxxD.DLL |
| |
MFCxxD.DLL MFCxx.DLL
où xx est le numéro de version ; par exemple, 42 représente la version 4.2.
Le MFCxx.dll est généralement le dernier dans la liste des ressources et des classes. MFCxx.dll inclut toutes les ressources MFC standard, y compris les chaînes d’invite pour tous les ID de commande standard. Le fait de le placer à la fin de la liste permet aux DLL et à l’application cliente elle-même de ne pas avoir leur propre copie des ressources MFC standard, mais de s’appuyer sur les ressources partagées dans le MFCxx.dll à la place.
La fusion des ressources et des noms de classes de toutes les DLL dans l’espace de noms de l’application cliente présente l’inconvénient de vous obliger à faire attention aux ID ou noms que vous choisissez.
L’exemple DLLHUSK gère l’espace de nom de ressource partagé à l’aide de plusieurs fichiers d’en-tête.
Si votre DLL d’extension MFC doit conserver des données supplémentaires pour chaque application, vous pouvez dériver une nouvelle classe de CDynLinkLibrary et la créer dans DllMain
. Lors de l’exécution, la DLL peut vérifier la liste des objets CDynLinkLibrary de l’application actuelle pour rechercher celle de cette DLL d’extension MFC particulière.