Partager via


TN033 : version DLL de MFC

Cette note décrit comment utiliser les MFCxx.DLL MFCxxD.DLL bibliothèques de liens dynamiques partagées (où xx est le numéro de version MFC) avec les applications MFC et les DLL d’extension MFC. Pour plus d’informations sur les DLL MFC standard, consultez Utilisation de MFC dans le cadre d’une DLL.

Cette note technique couvre trois aspects des DLL. Les deux derniers sont destinés aux utilisateurs les plus avancés :

Si vous souhaitez créer une DLL à l’aide de MFC qui peut être utilisée avec des applications non MFC (appelée DLL MFC standard), reportez-vous à la note technique 11.

Vue d’ensemble de la prise en charge de MFCxx.DLL : Terminologie et fichiers

DLL MFC standard : vous utilisez une DLL MFC standard pour générer une DLL autonome à l’aide de certaines classes MFC. Les interfaces entre les limites app/DLL sont des interfaces « C » et l’application cliente n’a pas besoin d’être une application MFC.

Les DLL MFC standard sont la version des DLL prises en charge dans MFC 1.0. Ils sont décrits dans la note technique 11 et l’exemple DLLScreenCapMFC Advanced Concepts .

Remarque

À partir de Visual C++ version 4.0, le terme USRDLL est obsolète et a été remplacé par une DLL MFC standard qui lie statiquement à MFC. Vous pouvez également créer une DLL MFC standard qui lie dynamiquement MFC à MFC.

MFC 3.0 (et versions ultérieures) prend en charge les DLL MFC standard avec toutes les nouvelles fonctionnalités, notamment les classes OLE et Database.

AFXDLL : également appelé version partagée des bibliothèques MFC. Il s’agit de la nouvelle prise en charge de la DLL ajoutée dans MFC 2.0. La bibliothèque MFC elle-même se trouve dans plusieurs DLL (décrites ci-dessous). Une application cliente ou une DLL lie dynamiquement les DLL dont elle a besoin. Les interfaces entre les limites d’application/DLL sont des interfaces de classe C++/MFC. L’application cliente DOIT être une application MFC. Cette DLL prend en charge toutes les fonctionnalités MFC 3.0 (exception : UNICODE n’est pas pris en charge pour les classes de base de données).

Remarque

À partir de Visual C++ version 4.0, ce type de DLL est appelé « DLL d’extension ».

Cette note permet MFCxx.DLL de faire référence à l’ensemble de dll MFC, qui inclut :

  • Déboguer : MFCxxD.DLL (combiné) et MFCSxxD.LIB (statique).

  • Version : MFCxx.DLL (combinée) et MFCSxx.LIB (statique).

  • Débogage Unicode : MFCxxUD.DLL (combiné) et MFCSxxD.LIB (statique).

  • Version Unicode : MFCxxU.DLL (combinée) et MFCSxxU.LIB (statique).

Remarque

Les MFCSxx[U][D].LIB bibliothèques sont utilisées conjointement avec les DLL partagées MFC. Ces bibliothèques contiennent du code qui doit être lié statiquement à l’application ou à la DLL.

Une application est liée aux bibliothèques d’importation correspondantes :

  • Debug: MFCxxD.LIB

  • Mise en production : MFCxx.LIB

  • Débogage Unicode : MFCxxUD.LIB

  • Version Unicode : MFCxxU.LIB

Une DLL d’extension MFC est une DLL qui s’étend MFCxx.DLL (ou les autres DLL partagées MFC). Ici, l’architecture des composants MFC se lance. Si vous dérivez une classe utile d’une classe MFC ou créez un autre kit de ressources de type MFC, vous pouvez le placer dans une DLL. Votre DLL utilise MFCxx.DLL, comme le fait l’application cliente ultime. Une DLL d’extension MFC autorise les classes feuilles réutilisables, les classes de base réutilisables et les classes de vue et de document réutilisables.

Avantages et inconvénients

Pourquoi utiliser la version partagée de MFC

  • L’utilisation de la bibliothèque partagée peut entraîner des applications plus petites. (Une application minimale qui utilise la plupart de la bibliothèque MFC est inférieure à 10 Ko).

  • La version partagée de MFC prend en charge les DLL d’extension MFC et les DLL MFC standard.

  • Il est plus rapide de créer une application qui utilise les bibliothèques MFC partagées qu’une application MFC liée statiquement. C’est parce qu’il n’est pas nécessaire de lier MFC lui-même. Il est particulièrement vrai dans les DEBUG builds où l’éditeur de liens doit compacter les informations de débogage. Lorsque votre application est liée à une DLL qui contient déjà les informations de débogage, il existe moins d’informations de débogage à compacter.

Pourquoi ne pas utiliser la version partagée de MFC :

  • L’expédition d’une application qui utilise la bibliothèque partagée nécessite que vous expédiiez MFCxx.DLL et d’autres bibliothèques avec votre programme. MFCxx.DLL est librement redistribuable comme de nombreuses DLL, mais vous devez toujours installer la DLL dans votre programme d’installation. En outre, vous devrez expédier les autres bibliothèques redistribuables utilisées par votre programme et les DLL MFC elles-mêmes.

Comment écrire une DLL d’extension MFC

Une DLL d’extension MFC est une DLL qui contient des classes et des fonctions pour développer les fonctionnalités des classes MFC. Une DLL d’extension MFC utilise les DLL MFC partagées de la même façon qu’une application les utilise, avec quelques considérations supplémentaires :

  • Le processus de génération est similaire à la création d’une application qui utilise les bibliothèques MFC partagées avec quelques options supplémentaires du compilateur et de l’éditeur de liens.

  • Une DLL d’extension MFC n’a pas de CWinAppclasse dérivée.

  • Une DLL d’extension MFC doit fournir un fichier spécial DllMain. AppWizard fournit une DllMain fonction que vous pouvez modifier.

  • Une DLL d’extension MFC fournit normalement une routine d’initialisation pour créer un CDynLinkLibraryfichier , si la DLL d’extension MFC exporte CRuntimeClass des types ou des ressources vers l’application. Une classe dérivée de CDynLinkLibrary peut être utilisée si les données par application doivent être conservées par la DLL d’extension MFC.

Ces considérations sont décrites plus en détail ci-dessous. Reportez-vous également à l’exemple DLLHUSKMFC Advanced Concepts . Il montre comment :

  • Générez une application à l’aide des bibliothèques partagées. (DLLHUSK.EXE est une application MFC qui lie dynamiquement les bibliothèques MFC et d’autres DLL.)

  • Générez une DLL d’extension MFC. (Il montre comment des indicateurs spéciaux tels que _AFXEXT l’utilisation sont utilisées lors de la génération d’une DLL d’extension MFC.)

  • Créez deux exemples de DLL d’extension MFC. L’une montre la structure de base d’une DLL d’extension MFC avec des exportations limitées (TESTDLL1) et l’autre montre l’exportation d’une interface de classe entière (TESTDLL2).

L’application cliente et toutes les DLL d’extension MFC doivent utiliser la même version de MFCxx.DLL. Suivez les conventions des DLL MFC et fournissez à la fois une version de débogage et de mise en production (/release) de votre DLL d’extension MFC. Cette pratique permet aux programmes clients de générer à la fois des versions de débogage et de mise en production de leurs applications et de les lier à la version de débogage ou de mise en production appropriée de toutes les DLL.

Remarque

Étant donné que le nom C++ mangling et les problèmes d’exportation, la liste d’exportation à partir d’une DLL d’extension MFC peut être différente entre les versions de débogage et de mise en production des mêmes DLL et DLL pour différentes plateformes. La version MFCxx.DLL comporte environ 2000 points d’entrée exportés ; le débogage MFCxxD.DLL comporte environ 3 000 points d’entrée exportés.

Remarque rapide sur la gestion de la mémoire

La section intitulée « Gestion de la mémoire », à la fin de cette note technique, décrit l’implémentation de la MFCxx.DLL version partagée de MFC. Les informations que vous devez savoir pour implémenter uniquement une DLL d’extension MFC sont décrites ici.

MFCxx.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. Il est important, car les bibliothèques DLL non-MFC et les DLL MFC régulières qui relient statiquement MFC à MFC font exactement l’inverse : chaque DLL alloue hors de son propre pool de mémoire.

Si une DLL d’extension MFC alloue de la mémoire, cette mémoire peut librement se mélanger avec n’importe quel autre objet alloué à l’application. En outre, si une application qui utilise les bibliothèques MFC partagées se bloque, le système d’exploitation conserve l’intégrité de toute autre application MFC qui partage la DLL.

De même, d’autres états MFC « globaux » tels que le fichier exécutable actuel à partir duquel charger des ressources, sont également partagés entre l’application cliente, toutes les DLL d’extension MFC et MFCxx.DLL lui-même.

Génération d’une DLL d’extension MFC

Vous pouvez utiliser AppWizard pour créer un projet DLL d’extension MFC et générer automatiquement les paramètres appropriés du compilateur et de l’éditeur de liens. Elle génère également une DllMain fonction que vous pouvez modifier.

Si vous convertissez un projet existant en DLL d’extension MFC, commencez par les paramètres standard générés à l’aide de la version partagée de MFC. Ensuite, effectuez les modifications suivantes :

  • Ajouter /D_AFXEXT aux indicateurs du compilateur. Dans la boîte de dialogue Propriétés du projet, sélectionnez la catégorie de préprocesseur C/C++>. Ajoutez _AFXEXT au champ Définir des macros , en séparant chacun des éléments avec des points-virgules.

  • Supprimez le commutateur du /Gy compilateur. Dans la boîte de dialogue Propriétés du projet, sélectionnez la catégorie Génération de code C/C++>. Vérifiez que la propriété Activer la liaison au niveau de la fonction n’est pas activée. Ce paramètre facilite l’exportation de classes, car l’éditeur de liens ne supprime pas les fonctions non référencées. Si le projet d’origine a généré une DLL MFC standard liée statiquement à MFC, remplacez l’option /MT /MD du compilateur (ou/MTd) par (ou /MDd).

  • Générez une bibliothèque d’exportation avec l’option /DLL LINK. Cette option est définie lorsque vous créez une cible et spécifiez la bibliothèque de liens dynamiques Win32 comme type cible.

Modification de vos fichiers d’en-tête

L’objectif habituel d’une DLL d’extension MFC est d’exporter certaines fonctionnalités courantes vers une ou plusieurs applications qui peuvent utiliser cette fonctionnalité. Essentiellement, la DLL exporte les classes et les fonctions globales à utiliser par vos applications clientes.

Pour vous assurer que chaque fonction membre est marquée comme appropriée pour l’importation ou l’exportation, utilisez les déclarations __declspec(dllexport) spéciales et __declspec(dllimport). Lorsque les applications clientes utilisent vos classes, vous souhaitez qu’elles soient déclarées en tant que __declspec(dllimport). Lorsque la DLL d’extension MFC elle-même est générée, les fonctions doivent être déclarées en tant que __declspec(dllexport). La DLL générée doit également exporter les fonctions, afin que les programmes clients puissent les lier au moment du chargement.

Pour exporter l’intégralité de votre classe, utilisez AFX_EXT_CLASS la définition de classe. L’infrastructure définit cette macro comme __declspec(dllexport) quand _AFXDLL et _AFXEXT est définie, mais elle la définit comme __declspec(dllimport) si _AFXEXT elle n’est pas définie. _AFXEXT est défini uniquement lors de la génération de votre DLL d’extension MFC. Par exemple :

class AFX_EXT_CLASS CExampleExport : public CObject
{ /* ... class definition ... */ };

Ne pas exporter la classe entière

Parfois, vous souhaiterez peut-être exporter uniquement les membres nécessaires individuels de votre classe. Par exemple, si vous exportez une CDialogclasse dérivée -, vous devrez peut-être exporter le constructeur et l’appel DoModal . Vous pouvez exporter ces membres à l’aide du fichier DEF de la DLL, mais vous pouvez également l’utiliser AFX_EXT_CLASS de la même façon sur les membres individuels que vous devez exporter.

Par exemple :

class CExampleDialog : public CDialog
{
public:
    AFX_EXT_CLASS CExampleDialog();
    AFX_EXT_CLASS int DoModal();
    // rest of class definition
    // ...
};

Lorsque vous le faites, vous risquez d’rencontrer un problème supplémentaire, car vous n’exportez pas tous les membres de la classe. Le problème est de la façon dont les macros MFC fonctionnent. Plusieurs macros d’assistance de MFC déclarent ou définissent des membres de données. Votre DLL doit également exporter ces membres de données.

Par exemple, la macro DECLARE_DYNAMIC est définie comme suit lors de la génération d’une DLL d’extension MFC :

#define DECLARE_DYNAMIC(class_name) \
protected: \
    static CRuntimeClass* PASCAL _GetBaseClass(); \
    public: \
    static AFX_DATA CRuntimeClass class##class_name; \
    virtual CRuntimeClass* GetRuntimeClass() const; \

La ligne qui commence static AFX_DATA déclare un objet statique à l’intérieur de votre classe. Pour exporter cette classe correctement et accéder aux informations d’exécution à partir d’un EXE client, vous devez exporter cet objet statique. Étant donné que l’objet statique est déclaré avec le modificateur AFX_DATA, vous devez uniquement définir AFX_DATA comme __declspec(dllexport) lorsque vous générez votre DLL. Définissez-le comme __declspec(dllimport) lorsque vous générez votre exécutable client.

Comme indiqué ci-dessus, AFX_EXT_CLASS il est déjà défini de cette façon. Il vous suffit de redéfinir AFX_DATA pour qu’il soit identique à la définition de AFX_EXT_CLASS votre classe.

Par exemple :

#undef  AFX_DATA
#define AFX_DATA AFX_EXT_CLASS
class CExampleView : public CView
{
    DECLARE_DYNAMIC()
    // ... class definition ...
};
#undef  AFX_DATA
#define AFX_DATA

MFC utilise toujours le AFX_DATA symbole sur les éléments de données qu’il définit dans ses macros. Cette technique fonctionne donc pour tous ces scénarios. Par exemple, elle fonctionnera pour DECLARE_MESSAGE_MAP.

Remarque

Si vous exportez la classe entière plutôt que les membres sélectionnés de la classe, les membres de données statiques sont automatiquement exportés.

Vous pouvez utiliser la même technique pour exporter automatiquement l’opérateur CArchive d’extraction pour les classes qui utilisent les macros DECLARE_SERIAL et IMPLEMENT_SERIAL. Exportez l’opérateur archive en crochetant les déclarations de classe (situées dans le fichier d’en-tête) avec le code suivant :

#undef AFX_API
#define AFX_API AFX_EXT_CLASS

/* your class declarations here */

#undef AFX_API
#define AFX_API

Limitations de _AFXEXT

Vous pouvez utiliser le _AFXEXT symbole de préprocesseur pour vos DLL d’extension MFC tant que vous n’avez pas plusieurs couches de DLL d’extension MFC. Si vous avez des DLL d’extension MFC qui appellent ou dérivent de classes dans vos propres DLL d’extension MFC, qui dérivent ensuite des classes MFC, vous devez utiliser votre propre symbole de préprocesseur pour éviter toute ambiguïté.

Le problème est que dans Win32, vous devez déclarer explicitement toutes les données pour l’exporter __declspec(dllexport) à partir d’une DLL et __declspec(dllimport) pour l’importer à partir d’une DLL. Lorsque vous définissez _AFXEXT, les en-têtes MFC vérifient qu’ils AFX_EXT_CLASS sont définis correctement.

Lorsque vous avez plusieurs couches, un symbole tel qu’il AFX_EXT_CLASS n’est pas suffisant : une DLL d’extension MFC peut exporter ses propres classes et importer également d’autres classes à partir d’une autre DLL d’extension MFC. Pour résoudre ce problème, utilisez un symbole de préprocesseur spécial qui indique que vous générez la DLL elle-même, au lieu d’utiliser la DLL. Par exemple, imaginez deux DLL d’extension MFC, A.DLLet B.DLL. Ils exportent chacune des classes dans A.H et B.H, respectivement. B.DLL utilise les classes de A.DLL. Les fichiers d’en-tête ressemblent à ceci :

/* A.H */
#ifdef A_IMPL
    #define CLASS_DECL_A   __declspec(dllexport)
#else
    #define CLASS_DECL_A   __declspec(dllimport)
#endif

class CLASS_DECL_A CExampleA : public CObject
{ /* ... class definition ... */ };

/* B.H */
#ifdef B_IMPL
    #define CLASS_DECL_B   __declspec(dllexport)
#else
    #define CLASS_DECL_B   __declspec(dllimport)
#endif

class CLASS_DECL_B CExampleB : public CExampleA
{ /* ... class definition ... */ };

Quand A.DLL elle est générée, elle est générée avec /DA_IMPL et quand B.DLL elle est générée, elle est générée avec /DB_IMPL. En utilisant des symboles distincts pour chaque DLL, CExampleB est exporté et CExampleA est importé lors de la génération B.DLL. CExampleA est exporté lors de la génération A.DLL et de l’importation lorsqu’il est utilisé par B.DLL ou par un autre client.

Ce type de superposition ne peut pas être effectué lors de l’utilisation des symboles intégrés AFX_EXT_CLASS et _AFXEXT de préprocesseur. La technique décrite ci-dessus résout ce problème de la même façon que MFC. MFC utilise cette technique lors de la création de ses DLL d’extension OLE, Database et Network MFC.

Toujours pas exporter la classe entière

Là encore, vous devrez prendre des précautions particulières lorsque vous n’exportez pas une classe entière. Vérifiez que les éléments de données nécessaires créés par les macros MFC sont exportés correctement. Vous pouvez le faire en redéfinissant AFX_DATA la macro de votre classe spécifique. Redéfinissez-le chaque fois que vous n’exportez pas la classe entière.

Par exemple :

// A.H
#ifdef A_IMPL
    #define CLASS_DECL_A  _declspec(dllexport)
#else
    #define CLASS_DECL_A  _declspec(dllimport)
#endif

#undef  AFX_DATA
#define AFX_DATA CLASS_DECL_A

class CExampleA : public CObject
{
    DECLARE_DYNAMIC()
    CLASS_DECL_A int SomeFunction();
    // class definition
    // ...
};

#undef AFX_DATA
#define AFX_DATA

DllMain

Voici le code que vous devez placer dans votre fichier source principal pour votre DLL d’extension MFC. Il doit venir après la norme inclut. Lorsque vous utilisez AppWizard pour créer des fichiers de démarrage pour une DLL d’extension MFC, il fournit un DllMain pour vous.

#include "afxdllx.h"

static AFX_EXTENSION_MODULE extensionDLL;

extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID)
{
   if (dwReason == DLL_PROCESS_ATTACH)
   {
      // MFC extension DLL one-time initialization
      if (!AfxInitExtensionModule(
             extensionDLL, hInstance))
         return 0;

      // TODO: perform other initialization tasks here
   }
   else if (dwReason == DLL_PROCESS_DETACH)
   {
      // MFC extension DLL per-process termination
      AfxTermExtensionModule(extensionDLL);

      // TODO: perform other cleanup tasks here
   }
   return 1;   // ok
}

L’appel pour AfxInitExtensionModule capturer les classes runtime (CRuntimeClass structures) du module et ses fabriques d’objets (COleObjectFactory objets) à utiliser ultérieurement lors de la création de l’objet CDynLinkLibrary . L’appel (facultatif) permet à AfxTermExtensionModule MFC de propre la DLL d’extension MFC lorsque chaque processus se détache (ce qui se produit lorsque le processus se termine, ou lorsque la DLL est déchargée par un FreeLibrary appel) à partir de la DLL d’extension MFC. Étant donné que la plupart des DLL d’extension MFC ne sont pas chargées dynamiquement (normalement, elles sont liées via leurs bibliothèques d’importation), l’appel à AfxTermExtensionModule généralement n’est pas nécessaire.

Si votre application charge et libère dynamiquement des DLL d’extension MFC, veillez à appeler AfxTermExtensionModule comme indiqué ci-dessus. Veillez également à utiliser AfxLoadLibrary et AfxFreeLibrary (au lieu de fonctions LoadLibrary Win32 et FreeLibrary) si votre application utilise plusieurs threads ou si elle charge dynamiquement une DLL d’extension MFC. L’utilisation AfxLoadLibrary et AfxFreeLibrary l’insures que le code de démarrage et d’arrêt qui s’exécute lorsque la DLL d’extension MFC est chargée et déchargée n’endommage pas l’état MFC global.

Le fichier d’en-tête AFXDLLX.H contient des définitions spéciales pour les structures utilisées dans les DLL d’extension MFC, telles que la définition pour AFX_EXTENSION_MODULE et CDynLinkLibrary.

L’extension GLOBALEDLL doit être déclarée comme indiqué. Contrairement à la version 16 bits de MFC, vous pouvez allouer de la mémoire et appeler des fonctions MFC pendant ce temps, car l’initialisation MFCxx.DLL complète est effectuée au moment de votre DllMain appel.

Partage de ressources et de classes

Les DLL d’extension MFC simples n’ont besoin que d’exporter quelques fonctions à faible bande passante vers l’application cliente et rien de plus. Des DLL plus gourmandes en interface utilisateur peuvent souhaiter exporter des ressources et des classes C++ vers l’application cliente.

L’exportation de ressources s’effectue via une liste de ressources. Dans chaque application, il s’agit d’une liste d’objets CDynLinkLibrary liée de manièreing. 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, s’il n’est pas trouvé, parcourez la liste des CDynLinkLibrary objets qui tentent de charger la ressource demandée.

La création dynamique d’objets C++ avec un nom de classe C++ est similaire. Le mécanisme de désérialisation d’objet CRuntimeClass MFC doit avoir tous les objets inscrits afin qu’il puisse reconstruire en créant dynamiquement l’objet C++ du type requis en fonction de ce qui a été stocké précédemment.

Si vous souhaitez que l’application cliente utilise des classes dans votre DLL d’extension MFC, DECLARE_SERIALvous devez exporter vos classes pour les rendre visibles dans l’application cliente. C’est également fait en parcourant la CDynLinkLibrary liste.

Dans l’exemple DLLHUSKConcepts avancés MFC, la liste ressemble à ceci :

head ->   DLLHUSK.EXE   - or - DLLHUSK.EXE
               |                    |
          TESTDLL2.DLL         TESTDLL2.DLL
               |                    |
          TESTDLL1.DLL         TESTDLL1.DLL
               |                    |
               |                    |
           MFC90D.DLL           MFC90.DLL

L’entrée MFCxx.DLL est généralement disponible en 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 à la fois aux DLL et à l’application cliente elle-même de s’appuyer sur les ressources partagées dans le MFCxx.DLL, au lieu d’avoir leurs propres copies.

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 que vous devez faire attention aux ID ou noms que vous choisissez. Vous pouvez désactiver cette fonctionnalité en n’exportant pas vos ressources ou un CDynLinkLibrary objet vers l’application cliente. L’exemple DLLHUSK gère l’espace de nom de ressource partagé à l’aide de plusieurs fichiers d’en-tête. Pour plus d’informations sur l’utilisation de fichiers de ressources partagés, consultez la Note technique 35 .

Initialisation de la DLL

Comme mentionné ci-dessus, vous souhaiterez généralement créer un CDynLinkLibrary objet pour exporter vos ressources et classes vers l’application cliente. Vous devez fournir un point d’entrée exporté pour initialiser la DLL. Minimalement, c’est une void routine qui ne prend pas d’arguments et ne retourne rien, mais il peut être tout ce que vous aimez.

Chaque application cliente qui souhaite utiliser votre DLL doit appeler cette routine d’initialisation si vous utilisez cette approche. Vous pouvez également allouer cet CDynLinkLibrary objet dans votre DllMain juste après l’appel AfxInitExtensionModule.

La routine d’initialisation doit créer un CDynLinkLibrary objet dans le tas de l’application actuelle, câblée à vos informations DLL d’extension MFC. Vous pouvez le faire en définissant une fonction comme celle-ci :

extern "C" extern void WINAPI InitXxxDLL()
{
    new CDynLinkLibrary(extensionDLL);
}

Le nom de routine, InitXxxDLL dans cet exemple, peut être tout ce que vous voulez. Il n’a pas besoin d’être extern "C", mais il facilite la maintenance de la liste d’exportation.

Remarque

Si vous utilisez votre DLL d’extension MFC à partir d’une DLL MFC standard, vous devez exporter cette fonction d’initialisation. Cette fonction doit être appelée à partir de la DLL MFC standard avant d’utiliser des classes ou ressources DLL d’extension MFC.

Exportation d’entrées

La façon simple d’exporter vos classes consiste à utiliser __declspec(dllimport) et __declspec(dllexport) sur chaque fonction globale et de classe que vous souhaitez exporter. Il est beaucoup plus facile, mais il est moins efficace que de nommer chaque point d’entrée dans un fichier DEF, comme décrit ci-dessous. C’est parce que vous avez moins de contrôle sur les fonctions exportées. Et vous ne pouvez pas exporter les fonctions par ordinal. TESTDLL1 et TESTDLL2 utilisez cette méthode pour exporter leurs entrées.

Une méthode plus efficace consiste à exporter chaque entrée en l’nommant dans le fichier DEF. Cette méthode est utilisée par MFCxx.DLL. Étant donné que nous exportons de manière sélective à partir de notre DLL, nous devons décider quelles interfaces particulières nous souhaitons exporter. Il est difficile, car vous devez spécifier les noms bascules vers l’éditeur de liens sous la forme d’entrées dans le fichier DEF. N’exportez aucune classe C++, sauf si vous avez vraiment besoin d’avoir un lien symbolique pour celui-ci.

Si vous avez essayé d’exporter des classes C++ avec un fichier DEF précédemment, vous pouvez développer un outil pour générer cette liste automatiquement. Elle peut être effectuée à l’aide d’un processus de liaison en deux étapes. Liez votre DLL une fois sans exportation et autorisez l’éditeur de liens à générer un fichier MAP. Le fichier MAP contient une liste de fonctions qui doivent être exportées. Avec une réorganisation, vous pouvez l’utiliser pour générer les entrées EXPORT pour votre fichier DEF. La liste d’exportation pour MFCxx.DLL et les DLL d’extension OLE et Database MFC, plusieurs milliers en nombre, a été générée avec un tel processus (bien qu’il ne soit pas entièrement automatique et nécessite un réglage de main toutes les fois en un certain temps).

CWinApp vs CDynLinkLibrary

Une DLL d’extension MFC n’a pas d’objet CWinAppdérivé de lui-même. Au lieu de cela, elle doit fonctionner avec l’objet CWinAppdé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.

Si votre DLL d’extension MFC doit conserver des données supplémentaires pour chaque application, vous pouvez dériver une nouvelle classe et CDynLinkLibrary la créer dans la InitXxxDLL routine décrite ci-dessus. Lors de l’exécution, la DLL peut case activée la liste d’objets de CDynLinkLibrary l’application actuelle pour trouver celle de cette DLL d’extension MFC particulière.

Utilisation de ressources dans votre implémentation DLL

Comme mentionné ci-dessus, la charge de ressource par défaut guide la liste des CDynLinkLibrary objets qui recherchent le premier EXE ou DLL qui a la ressource demandée. Toutes les API MFC et tout le code interne utilise AfxFindResourceHandle pour parcourir la liste des ressources pour rechercher n’importe quelle ressource, quel que soit l’emplacement où elle se trouve.

Si vous souhaitez charger uniquement des ressources à partir d’un emplacement spécifique, utilisez les API 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. L’exemple TESTDLL2 utilise cette approche pour charger explicitement un menu.

La marche à pied de la liste présente quelques inconvénients : il est légèrement plus lent 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 trouve d’abord la ressource, ou NULL.

Écriture d’une application qui utilise la version dll

Conditions requises pour l’application

Une application qui utilise la version partagée de MFC doit suivre quelques règles de base :

  • Il doit avoir un CWinApp objet et respecter les règles standard d’une pompe de messages.

  • Elle doit être compilée avec un ensemble d’indicateurs de compilateur requis (voir ci-dessous).

  • Il doit être lié aux bibliothèques d’importation MFCxx. En définissant les indicateurs de compilateur requis, les en-têtes MFC déterminent au moment du lien la bibliothèque avec laquelle l’application doit établir un lien.

  • Pour exécuter l’exécutable, MFCxx.DLL doit se trouver sur le chemin d’accès ou dans le répertoire système Windows.

Création avec l’environnement de développement

Si vous utilisez le makefile interne avec la plupart des valeurs par défaut standard, vous pouvez facilement modifier le projet pour générer la version dll.

L’étape suivante suppose que vous disposez d’une application MFC qui fonctionne correctement avec NAFXCWD.LIB (pour le débogage) et (pour la version) et NAFXCW.LIB que vous souhaitez la convertir pour utiliser la version partagée de la bibliothèque MFC. Vous exécutez l’environnement Visual Studio et disposez d’un fichier projet interne.

  1. Dans le menu Projets , sélectionnez Propriétés. Dans la page Général sous Paramètres par défaut du projet, définissez les classes Microsoft Foundation pour utiliser MFC dans une DLL partagée (MFCxx(d).dll).

Création avec NMAKE

Si vous utilisez la fonctionnalité makefile externe du compilateur ou que vous utilisez NMAKE directement, vous devez modifier votre makefile pour prendre en charge les options requises du compilateur et de l’éditeur de liens.

Indicateurs de compilateur requis :

  • /D_AFXDLL /MD /D_AFXDLL

Les en-têtes MFC standard doivent _AFXDLL être définis.

  • /MD L’application doit utiliser la version DLL de la bibliothèque d’exécution C.

Tous les autres indicateurs du compilateur suivent les valeurs par défaut MFC (par exemple, _DEBUG pour le débogage).

Modifiez la liste des bibliothèques de l’éditeur de liens. Remplacez NAFXCWD.LIB par MFCxxD.LIB et NAFXCW.LIB par MFCxx.LIB. Remplacez LIBC.LIB par MSVCRT.LIB. Comme pour toute autre bibliothèque MFC, il est important qu’elle MFCxxD.LIB soit placée avant toutes les bibliothèques C-runtime.

Vous pouvez éventuellement ajouter /D_AFXDLL à vos options de compilateur de ressources de mise en production et de débogage (celle qui compile réellement les ressources avec /R). Cette option réduit la taille de votre exécutable final en partageant les ressources présentes dans les DLL MFC.

Une reconstruction complète est requise une fois ces modifications effectuées.

Génération des exemples

La plupart des exemples de programmes MFC peuvent être générés à partir de Visual C++ ou à partir d’un MAKEFILE compatible NMAKE partagé à partir de la ligne de commande.

Pour convertir l’un de ces exemples à utiliser MFCxx.DLL, vous pouvez charger le fichier MAK dans Visual C++ et définir les options project comme décrit ci-dessus. Si vous utilisez la build NMAKE, vous pouvez spécifier AFXDLL=1 sur la ligne de commande NMAKE et générer l’exemple à l’aide des bibliothèques MFC partagées.

L’exemple De concepts avancés MFC DLLHUSK est généré avec la version DLL de MFC. Cet exemple montre non seulement comment créer une application liée MFCxx.DLL, mais également d’autres fonctionnalités de l’option d’empaquetage dll MFC, comme les DLL d’extension MFC décrites plus loin dans cette note technique.

Notes d’empaquetage

Les versions de version des DLL (MFCxx.DLL et MFCxxU.DLL) sont redistribuables librement. Les versions de débogage des DLL ne sont pas redistribuables librement et doivent être utilisées uniquement pendant le développement de votre application.

Les DLL de débogage sont fournies avec des informations de débogage. À l’aide du débogueur Visual C++, vous pouvez suivre l’exécution de votre application et de la DLL. Les DLL release (MFCxx.DLL et MFCxxU.DLL) ne contiennent pas d’informations de débogage.

Si vous personnalisez ou régénérez les DLL, vous devez les appeler autre chose que « MFCxx ». Le fichier MFCDLL.MAK SRC MFC décrit les options de génération et contient la logique permettant de renommer la DLL. Le renommage des fichiers est nécessaire, car ces DLL sont potentiellement partagées par de nombreuses applications MFC. Une version personnalisée des DLL MFC remplace celles installées sur le système peut interrompre une autre application MFC à l’aide des DLL MFC partagées.

La reconstruction des DLL MFC n’est pas recommandée.

Implémentation du fichier MFCxx.DLL

La section suivante décrit comment la DLL MFC (MFCxx.DLL et MFCxxD.DLL) est implémentée. Comprendre les détails ici n’est pas important si tout ce que vous souhaitez faire est d’utiliser la DLL MFC avec votre application. Les détails ici ne sont pas essentiels pour comprendre comment écrire une DLL d’extension MFC, mais la compréhension de cette implémentation peut vous aider à écrire votre propre DLL.

Vue d’ensemble de l’implémentation

La DLL MFC est vraiment un cas particulier d’une DLL d’extension MFC, comme décrit ci-dessus. Il a un grand nombre d’exportations pour un grand nombre de classes. Il existe quelques choses supplémentaires que nous faisons dans la DLL MFC qui le rendent encore plus spécial qu’une DLL d’extension MFC standard.

Win32 fait la plupart du travail

La version 16 bits de MFC nécessite un certain nombre de techniques spéciales, notamment les données par application sur le segment de pile, les segments spéciaux créés par le code d’assembly 80x86, les contextes d’exception par processus et d’autres techniques. Win32 prend directement en charge les données par processus dans une DLL, ce qui est ce que vous souhaitez la plupart du temps. Pour la plupart MFCxx.DLL , il suffit d’empaquetage NAFXCW.LIB dans une DLL. Si vous examinez le code source MFC, vous trouverez quelques #ifdef _AFXDLL cas, car il n’y a pas beaucoup de cas spéciaux qui doivent être effectués. Les cas spéciaux qui existent spécifiquement pour traiter Win32 sur Windows 3.1 (autrement appelé Win32s). Win32s ne prend pas directement en charge les données DLL par processus. La DLL MFC doit utiliser les API Win32 (Thread-Local Storage) pour obtenir les données locales du processus.

Impact sur les sources de bibliothèque, fichiers supplémentaires

L’impact de la version sur les _AFXDLL sources et en-têtes de bibliothèque de classes MFC normaux est relativement mineur. Il existe un fichier de version spécial (AFXV_DLL.H) et un fichier d’en-tête supplémentaire (AFXDLL_.H) inclus par l’en-tête principal AFXWIN.H . L’en-tête AFXDLL_.H inclut la CDynLinkLibrary classe et d’autres détails d’implémentation des DLL d’extension _AFXDLL MFC et des applications. L’en-tête AFXDLLX.H est fourni pour créer des DLL d’extension MFC (voir ci-dessus pour plus d’informations).

Les sources régulières de la bibliothèque MFC dans MFC SRC ont un code conditionnel supplémentaire sous la _AFXDLL #ifdef. Un fichier source supplémentaire (DLLINIT.CPP) contient le code d’initialisation de DLL supplémentaire et d’autres colles pour la version partagée de MFC.

Pour générer la version partagée de MFC, des fichiers supplémentaires sont fournis. (Consultez ci-dessous pour plus d’informations sur la création de la DLL.)

  • Deux fichiers DEF sont utilisés pour exporter les points d’entrée DLL MFC pour les versions de débogage (MFCxxD.DEF) et de mise en production (MFCxx.DEF) de la DLL.

  • Un fichier RC (MFCDLL.RC) contient toutes les ressources MFC standard et une VERSIONINFO ressource pour la DLL.

  • Un fichier CLW (MFCDLL.CLW) est fourni pour permettre la navigation dans les classes MFC à l’aide de ClassWizard. Cette fonctionnalité n’est pas particulière à la version DLL de MFC.

Gestion de la mémoire

Une application utilisant MFCxx.DLL un allocateur de mémoire commun fourni par MSVCRTxx.DLL, la DLL du runtime C partagé. L’application, les DLL d’extension MFC et les DLL MFC utilisent cet allocateur de mémoire partagée. À l’aide d’une DLL partagée pour l’allocation de mémoire, les DLL MFC peuvent allouer de la mémoire libérée ultérieurement par l’application ou inversement. Étant donné que l’application et la DLL doivent utiliser le même allocateur, vous ne devez pas remplacer le global operator new C++ ou operator delete. Les mêmes règles s’appliquent au reste des routines d’allocation de mémoire au moment de l’exécution C (par mallocexemple, , reallocfreeet d’autres).

Noms Ordinals et classes __declspec(dllexport) et DLL

Nous n’utilisons pas les class__declspec(dllexport) fonctionnalités du compilateur C++. Au lieu de cela, une liste d’exportations est incluse avec les sources de bibliothèque de classes (MFCxx.DEF et MFCxxD.DEF). Seuls un ensemble de points d’entrée sélectionnés (fonctions et données) sont exportés. D’autres symboles, tels que les fonctions d’implémentation privée MFC ou les classes, ne sont pas exportés. Toutes les exportations sont effectuées par ordinal sans nom de chaîne dans la table de noms résident ou non résidente.

L’utilisation class__declspec(dllexport) peut être une alternative viable pour la création de DLL plus petites, mais dans une DLL volumineuse comme MFC, le mécanisme d’exportation par défaut a des limites d’efficacité et de capacité.

Cela signifie que nous pouvons empaqueter une grande quantité de fonctionnalités dans la version MFCxx.DLL qui n’est qu’environ 800 Ko sans compromettre une grande vitesse d’exécution ou de chargement. MFCxx.DLLaurait été 100 Ko plus grande si cette technique n’avait pas été utilisée. La technique permet d’ajouter des points d’entrée supplémentaires à la fin du fichier DEF. Il permet un contrôle de version simple sans compromettre la vitesse et l’efficacité de l’exportation par ordinal. Les révisions de version majeure dans la bibliothèque de classes MFC modifient le nom de la bibliothèque. Autrement dit, MFC30.DLL la DLL redistribuable contenant la version 3.0 de la bibliothèque de classes MFC. Une mise à niveau de cette DLL, par exemple, dans une MFC hypothétique 3.1, la DLL serait nommée MFC31.DLL à la place. Là encore, si vous modifiez le code source MFC pour produire une version personnalisée de la DLL MFC, utilisez un nom différent (et de préférence sans « MFC » dans le nom).

Voir aussi

Notes techniques par numéro
Notes techniques par catégorie