TN033 : Version DLL de MFC
Cette remarque explique comment utiliser les bibliothèques de liens dynamiques partagées MFCxx.DLL et MFCxxD.DLL (où x est le numéro de version MFC) avec les applications et les DLL d'extension MFC.Pour plus d'informations sur les DLL normales, consultez l' Utilisation de MFC dans le cadre d'une DLL.
Cette note technique couvre trois aspects des DLL.Les deux derniers sont pour plus d'utilisateurs expérimentés :
Comment vous générez une DLL d'extension MFC
Comment vous générez une application MFC qui utilise la version DLL de MFC
Comment les bibliothèques de liens dynamiques partagées par MFC sont implémentées
Si vous vous intéressez à la génération d'une DLL à l'aide de MFC qui peut être utilisé avec les applications non-MFC (c'est une DLL normale), reportez -vous à note technique 11.
Vue d'ensemble de la prise en charge de MFCxx.DLL : terminologie et fichiers
Regular DLL: Vous utilisez une DLL normale pour générer une DLL autonome en utilisant quelques-unes des classes MFC.Les interfaces à travers la limite d'App/DLL sont des interfaces « C », et l'application cliente ne doit pas être une application MFC.
Il s'agit de la version de la prise en charge de DLL prise en charge dans MFC 1,0.Il est décrit dans note technique 11 et les concepts avancés par MFC issus DLLScreenCap.
[!REMARQUE]
À partir de la version 4,0 de Visual C++, le terme USRDLL est obsolète et a été remplacé par une DLL normale liée de manière statique aux MFC.Vous pouvez également générer une DLL normale liée de manière dynamique aux MFC.
MFC 3,0 (et ci-dessus) prend en charge les DLL normales avec toute la nouvelle fonctionnalité notamment OLE et les classes de base de données.
AFXDLL: Cela est également appelée de la version partagée des bibliothèques MFC.Il s'agit de la nouvelle prise en charge de DLL ajoutée dans MFC 2,0.La bibliothèque MFC elle-même est dans un certain nombre de DLL (décrits ci-dessous) et une application cliente ou une DLL est liée de manière dynamique les DLL dont il a besoin.Les interfaces à travers la limite d'application/DLL sont des interfaces de classe de C++/MFC.L'application cliente DOIT être une application MFC.Cette opération 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 la version 4,0 de Visual C++, ce type de DLL est appelée « DLL d'extension. »
Cette remarque utilisera MFCxx.DLL pour faire référence au jeu entier de DLL MFC, notamment :
Débogage : MFCxxD.DLL (associée) et MFCSxxD.LIB (statique).
Version finale : MFCxx.DLL (associée) et MFCSxx.LIB (statique).
Débogage Unicode : MFCxxUD.DLL (associée) et MFCSxxD.LIB (statique).
Version Unicode : MFCxxU.DLL (associée) et MFCSxxU.LIB (statique).
[!REMARQUE]
Les bibliothèques de MFCSxx U [] [D] .LIB sont utilisées conjointement avec les DLL partagées par MFC.Ces bibliothèques contiennent le code qui doit être liée statiquement à l'application ou à la DLL.
Liens d'application vers des bibliothèques d'importation correspondantes :
Débogage : MFCxxD.LIB
Version finale : MFCxx.LIB
Débogage Unicode : MFCxxUD.LIB
Version Unicode : MFCxxU.LIB
« Une DLL d'extension MFC » est une DLL appuie sur MFCxx.DLL (et/ou les autres DLL partagées par MFC).Ici l'architecture du composant MFC entre en action.Si vous dérivez une classe utile d'une classe MFC, ou générez une autre ensemble d'outils comme un MFC, vous pouvez le placer dans une DLL.Si la DLL utilise MFCxx.DLL, comme le fait l'application cliente finale.Cela permet aux classes réutilisables de feuille, les classes de base réutilisables, et des classes réutilisables de vue/document.
Le pour et inconvénients
Pourquoi devez -vous utiliser la version partagée des MFC ?
L'utilisation de la bibliothèque partagée peut entraîner de plus petites applications (une application minimale que utilise plus de la bibliothèque MFC est moins que 10K).
La version partagée des DLL d'extension MFC MFC prend en charge et les DLL normales.
Générer une application qui utilise les bibliothèques MFC partagées est plus rapide que la génération d'une application de manière statique aux MFC car il n'est pas nécessaire de lier MFC elle-même.Ceci est particulièrement vrai dans les générations de DÉBOGAGE où l'éditeur de liens doit compacter les informations de débogage — en établissant une liaison avec une DLL qui contient déjà des informations de débogage, il est moins d'informations de débogage à compacter dans votre application.
Pourquoi vous ne devez pas utiliser la version partagée des MFC :
- Expédiant une application qui utilise la bibliothèque partagée requiert que vous de livrer la bibliothèque de MFCxx.DLL (etc.) avec votre programme.MFCxx.DLL est librement redistribuable comme de nombreuses DLL, mais vous devez toujours installer la DLL dans votre programme d'installation.De plus, vous devrez livrer le MSVCRTxx.DLL, qui contient la bibliothèque d'exécution c qui est utilisée par votre programme et les DLL MFC eux-mêmes.
Comment écrire une DLL d'extension MFC
Une DLL d'extension MFC est un de la classe conteneur et des fonctions d'une DLL écrites pour embellir 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 utilise il, avec quelques remarques supplémentaires :
Le processus de génération est semblable à générer une application qui utilise les bibliothèques MFC partagées avec certains compilateur supplémentaire et options de l'éditeur de liens.
Une DLL d'extension MFC n'a pas CWinAppclasse dérivée de.
Une DLL d'extension MFC doit fournir DllMainspécial.AppWizard fournit une fonction d' DllMain que vous pouvez modifier.
Une DLL d'extension MFC fournit généralement une routine d'initialisation pour créer CDynLinkLibrary si la DLL d'extension souhaite exporter CRuntimeClasses ou ressources à l'application.Une classe dérivée de CDynLinkLibrary peut être utilisée si des données d'application doivent être conservées par la DLL d'extension.
Ces considérations sont décrites plus en détail ci-dessous.Vous devez également faire référence à l'exemple DLLHUSK de concepts avancés par MFC comme il illustre :
Générer une application à l'aide de les bibliothèques partagées.(DLLHUSK.EXE est une application MFC liée de manière dynamique aux MFC libraries ainsi qu'à d'autres DLL.)
Générer une DLL d'extension MFC.(Notez que les balises spéciales telles que _AFXEXT utilisées lors de la génération d'une DLL d'extension)
Deux exemples des DLL d'extension MFC.Il illustre la structure de base d'une DLL d'extension MFC avec les exportations limitées (TESTDLL1) et l'autre montre exporter une interface de classe entière (TESTDLL2).
L'application cliente et toutes les DLL d'extension doivent utiliser la même version de MFCxx.DLL.Vous devez respecter la convention de la DLL MFC et fournir un débogage et vendre la version au détail (/release) de la DLL d'extension.Cela permet aux programmes clients pour générer les versions debug et commerciales de leurs applications et de les lier avec le débogage approprié ou la version commerciale de toutes les DLL.
[!REMARQUE]
Étant donné que les problèmes de supprimer et d'exportation C++, la liste d'exportation d'une DLL d'extension peuvent être différents selon les versions debug et commerciales du même DLL et DLL en fonction de la plateforme.La version commerciale MFCxx.DLL possède près de 2000 points d'entrée exportés ; le débogage MFCxxD.DLL possède près de 3000 points d'entrée exportés.
Remarque rapide sur la gestion de la mémoire
La section intitulée « gestion de la mémoire, » approche de la fin de cette note technique, décrit l'implémentation de MFCxx.DLL avec la version partagée des MFC.Les informations que vous devez connaître pour implémenter un seul DLL d'extension sont décrites ici.
MFCxx.DLL et toutes les DLL d'extension chargées dans l'espace d'adressage d'une application cliente utiliseront le même allocateur de mémoire, le chargement de ressources et d'autres conditions « globaux » MFC que si elles étaient dans la même application.Ceci est important car les bibliothèques non-MFC et les DLL normales de DLL liées de manière statique aux MFC font exactement le contraire et s'assurent que chaque DLL affecte une partie de son propre pool de mémoire.
Si une DLL d'extension alloue de la mémoire, cette mémoire peut alors être librement mélangée avec tout autre objet alloué par l'application.En outre, si une application qui utilise les bibliothèques MFC partagées se bloque, la protection du système d'exploitation contiendra l'intégrité de toute autre application MFC partageant la DLL.
De même d'autres états « globaux » MFC, comme le fichier exécutable en cours pour charger des ressources de, sont également partagés entre l'application cliente et toutes les DLL d'extension MFC ainsi que toutes elle-même.
Générer une DLL d'extension
Vous pouvez utiliser AppWizard pour créer un projet DLL d'extension MFC, et il génère automatiquement les paramètres appropriés du compilateur et de l'éditeur de liens.Elle était génèrent également une fonction d' DllMain que vous pouvez modifier.
Si vous convertissez un projet existant à une DLL d'extension MFC, commencez par les règles standard pour générer une application à l'aide de la version partagée des MFC, puis procédez comme suit :
Ajoutez /D_AFXEXT aux indicateurs de compilateur.Dans la boîte de dialogue de propriétés du projet, sélectionnez le nœud C/C++.Sélectionnez la catégorie de préprocesseur.Ajoutez _AFXEXT au champ de macros de définir, en séparant les éléments par des points-virgules.
Supprimez le commutateur de compilation d' /Gy .Dans la boîte de dialogue de propriétés du projet, sélectionnez le nœud C/C++.Sélectionnez la catégorie de génération de code.Assurez -vous que l'option « activez au niveau de les fonctions liaison » n'est pas activé.Cela facilitera exporter des classes car l'éditeur de liens ne supprimera pas de fonctions non référencées.Si le projet d'origine est utilisé pour générer une DLL normale liée de manière statique lié aux MFC, modifiez l'option du compilateur de /MT[d] à /MD[d].
Générez une bibliothèque d'exportation avec l'option de /DLL À LINK.Cela sera défini lorsque vous créez un nouveau cible, en spécifiant la bibliothèque de liens dynamiques Win32 comme type de cible.
Modifier les fichiers d'en-tête
Le but d'une DLL d'extension est généralement d'exporter une des fonctionnalités communes à une ou plusieurs applications qui peuvent utiliser cette fonctionnalité.Cela bout vers le bas à exporter des classes et des fonctions globales qui sont disponibles pour vos applications clientes.
Pour ce faire vous devez vous assurer que chacune des fonctions membres est marquée comme importation ou exportation de manière appropriée.Cela requiert des déclarations spéciales : __declspec(dllexport) et __declspec(dllimport).Lorsque vos classes sont utilisées par les applications clientes, vous voulez à déclarer comme __declspec(dllimport).Lorsque la DLL d'extension lui-même est généré, elles doivent être déclarées comme __declspec(dllexport).En outre, les fonctions doivent être exportés en fait, afin que les programmes clients sont liés à ces derniers au moment de le chargement.
Pour exporter votre classe entière, utilisez AFX_EXT_CLASS dans la définition de classe.Cette macro est définie par l'infrastructure comme __declspec(dllexport) lorsque _AFXDLL et _AFXEXT est défini, mais est définie comme __declspec(dllimport) lorsque _AFXEXT n'est pas défini._AFXEXT comme décrit ci-dessus, est défini uniquement lors de la génération de la DLL d'extension.Par exemple :
class AFX_EXT_CLASS CExampleExport : public CObject
{ ... class definition ... };
Non-exportation de la classe entière
Il peut être utile d'exporter uniquement les membres requis de la classe.Par exemple, si vous exportez une classe dérivée de CDialog, il se peut que vous n'ayez à exporter que le constructeur et l'appel à DoModal.Vous pouvez exporter ces membres à l'aide du fichier .DEF de la DLL, mais vous pouvez également utiliser AFX_EXT_CLASS pratiquement de la même façon aux différents membres à exporter.
Par exemple :
class CExampleDialog : public CDialog
{
public:
AFX_EXT_CLASS CExampleDialog();
AFX_EXT_CLASS int DoModal();
// rest of class definition
.
.
.
};
Dans ce cas, vous pouvez rencontrer un problème supplémentaire parce que vous n'exportez plus tous les membres de la classe.Le problème est de la manière dont les macros MFC.Plusieurs des macros d'assistance des MFC déclarent ou définissent en fait des données membres.Par conséquent, ces données membres doivent également être exportées de la DLL.
Par exemple, la macro DECLARE_DYNAMIC est définie de la façon suivante lors de la génération d'une DLL d'extension :
#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 par « AFX_DATAstatique » déclare un objet statique à l'intérieur de la classe.Pour exporter cette classe correctement et accéder aux informations d'exécution d'un client .EXE, vous devez exporter cet objet statique.Dans la mesure où l'objet statique est déclaré avec le modificateur AFX_DATA, vous devez définir AFX_DATA en tant que __declspec(dllexport) seulement lors de la génération de la DLL et le définir en tant que __declspec(dllimport) lors de la génération de l'exécutable client.
Comme décrit ci-dessus, AFX_EXT_CLASS est déjà définie de cette façon.Il vous suffit de redéfinir AFX_DATA pour être le même que AFX_EXT_CLASS autour de la définition de 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
Les MFC utilisent toujours le symbole d' AFX_DATA sur les éléments de données qu'il définit dans leurs macros, cette technique fonctionne pour tous les scénarios.Par exemple, elle s'exécutera pour DECLARE_MESSAGE_MAP.
[!REMARQUE]
Si vous exportez la classe entière plutôt qu'une sélection de membres de la classe, les données membres statiques sont exportées automatiquement
Vous pouvez utiliser la même technique pour exporter automatiquement l'opérateur d'extraction d' CArchive pour les classes qui utilisent les macros d' DECLARE_SERIAL et d' IMPLEMENT_SERIAL .Exportez l'opérateur d'archivage en délimitant les déclarations de classe (trouvent dans. Fichier de H) 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 symbole de préprocesseur d'AFXEXT _pour des DLL d'extension tant que vous n'avez pas plusieurs couches de DLL d'extension.Si vous avez des DLL d'extension qui appellent ou dérivent des classes dans vos propres DLL d'extension, lesquelles dérivent ensuite de 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 de manière explicite toutes les données en tant que __declspec(dllexport) si l'exportation s'effectue à partir d'une DLL, et en tant que __declspec(dllimport) si l'importation a lieu à partir d'une DLL.Lorsque vous définissez _AFXEXT, les en-têtes MFC doivent s'assurer que le symbole AFX_EXT_CLASS est défini correctement.
Lorsque vous avez plusieurs couches, un symbole tel qu' AFX_EXT_CLASS n'est pas suffisant, étant donné qu'une DLL d'extension peut exporter de nouvelles classes mais aussi importer d'autres classes d'une autre DLL d'extension.Pour traiter ce problème, utilisez un symbole de préprocesseur spécial qui indique que vous n'êtes pas en train d'utiliser la DLL.Par exemple, imaginez deux DLL d'extension, A.DLL, et B.DLL.Elles exportent chacune certaines classes dans A.H et B.H, respectivement.B.DLL utilise les classes d'A.DLL.Les fichiers d'en-tête ressembleraient à ce qui suit :
/* 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 .. };
Lorsque A.DLL est généré, il est généré avec /D A_IMPL et lorsque B.DLL est généré, il est généré avec /D B_IMPL.En utilisant des symboles séparés pour chaque DLL, CExampleB est exporté et CExampleA est importé génération B.DLL.CExampleA est exporté lors de la génération A.DLL et importé s'il est utilisé par B.DLL (ou un autre client).
Ce type de couche ne peut être effectuée lorsque vous utilisez AFX_EXT_CLASS et les symboles prédéfinis de préprocesseur d' _AFXEXT .La technique décrite ci-dessus corrige ce problème d'une manière pas contrairement aux utilise le mécanisme des MFC applique pour générer ses DLL d'extension OLE, de base de données, et réseau.
Non-exportation de la classe entière
Là encore, vous devez faire attention spéciale lorsque vous n'exportez pas une classe entière.Vous devez vous assurer que les éléments de données nécessaires créés par les macros MFC sont exportés correctement.Cette opération peut être effectuée en redéfinissant AFX_DATA à la macro spécifique de vos classes.Vous devez accomplir cette tâche lorsque 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 exact que vous devez placer dans votre fichier source principal de votre DLL d'extension.Il doit venir après que la norme inclue.Notez que lorsque vous utilisez AppWizard pour créer des fichiers de départ pour une DLL d'extension, il fournit 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)
{
// Extension DLL one-time initialization
if (!AfxInitExtensionModule(
extensionDLL, hInstance))
return 0;
// TODO: perform other initialization tasks here
}
else if (dwReason == DLL_PROCESS_DETACH)
{
// Extension DLL per-process termination
AfxTermExtensionModule(extensionDLL);
// TODO: perform other cleanup tasks here
}
return 1; // ok
}
L'appel à AfxInitExtensionModule capture les classes de runtime de modules (structures d'CRuntimeClass ) ainsi que ses fabriques d'objet (objets d'COleObjectFactory ) pour l'utiliser ultérieurement lorsque l'objet de CDynLinkLibrary est créé.L'appel (facultatif) à AfxTermExtensionModule permet aux MFC au nettoyage la DLL d'extension lorsque chaque processus se détache (ce qui se produit lorsque la fin du processus, ou lorsque la DLL est déchargée d'un appel de FreeLibrary ) de la DLL d'extension.La plupart des DLL d'extension ne sont pas chargées dynamiquement (généralement, ils sont liés via leurs bibliothèques d'importation), l'appel à AfxTermExtensionModule n'est généralement pas nécessaire.
Si votre application charge et libère les DLL d'extension dynamiquement, veillez à appeler AfxTermExtensionModule comme indiqué ci-dessus.Assurez -vous également d'utiliser AfxLoadLibrary et AfxFreeLibrary (au lieu de Win32 s'exécute LoadLibrary et FreeLibrary) si votre application utilise plusieurs threads ou si elle chargera dynamiquement une DLL d'extension.À l'aide de AfxLoadLibrary et AfxFreeLibrary assurer que le code de démarrage et d'arrêt qui s'exécute lorsque la DLL d'extension est chargé et du déchargement n'altère pas l'état global des MFC.
Le fichier d'en-tête AFXDLLX.H contient des définitions spéciales pour les structures utilisées dans les DLL d'extension, comme la définition de AFX_EXTENSION_MODULE et CDynLinkLibrary.
L'extensionDLL global doit être déclaré comme indiqué.Contrairement à la version 16 bits de MFC, vous pouvez allouer de la mémoire et appeler des fonctions MFC pendant ce temps, puisque MFCxx.DLL occupe complètement initialisé avant que votre DllMain ne soit appelée.
Partage des ressources et des classes
Les DLL d'extension simples MFC doivent seulement exporter des fonctions à bande passante restreinte à l'application cliente et à rien faire.Plus de DLL intensifs d'interface utilisateur peuvent souhaiter exporter des ressources et des classes C++ à l'application cliente.
L'exportation des ressources s'effectue par l'intermédiaire d'une liste de ressources.Dans chaque application est d'une liste liée d'objets de CDynLinkLibrary .Lors de la recherche d'une ressource, la plupart des implémentations MFC standard qui chargent des ressources examinent d'abord le module actuel de la ressource (AfxGetResourceHandle) et si a détecté une section décrivent la liste des objets de CDynLinkLibrary essayer de charger la ressource demandée.
La création dynamique d'objets C++ donnés le nom de classe C++ est similaire.Le mécanisme de désérialisation des objets MFC exige que tous les objets d' CRuntimeClass enregistrés afin qu'il puisse reconstruire en créant dynamiquement l'objet C++ du type requis sur ce qui a été stocké précédemment.
Si vous souhaitez que l'application cliente afin d'utiliser des classes dans votre DLL d'extension qui sont DECLARE_SERIAL, vous devez exporter vos classes soit visible par l'application cliente.Il est également faire en parcourir la liste de CDynLinkLibrary .
Dans le cas de MFC les concepts avancés issus DLLHUSK, la liste ressemble à ceci :
head -> DLLHUSK.EXE - or - DLLHUSK.EXE
| |
TESTDLL2.DLL TESTDLL2.DLL
| |
TESTDLL1.DLL TESTDLL1.DLL
| |
| |
MFC90D.DLL MFC90.DLL
MFCxx.DLL occupe généralement en dernier dans la liste des ressources et des classes.MFCxx.DLL inclut toutes les ressources MFC standard, telles que des chaînes d'invite de tous les ID de commande standard.Le placer à la fin de la liste permet DLL et l'application cliente elle-même de ne pas avoir de leur propre copie des ressources MFC standard, mais de vous reposer sur les ressources partagées dans le MFCxx.DLL à la place.
Fusionner les ressources et les noms de classes de toutes les DLL dans l'espace de noms de l'application cliente l'inconvénient est que vous devez faire attention les ID ou vous nomme sélection.Vous pouvez évidemment désactiver cette fonctionnalité en n'exportez pas vos ressources ou un objet de CDynLinkLibrary à l'application cliente.L'exemple DLLHUSK gère l'espace de noms des ressources partagées en utilisant plusieurs fichiers d'en-tête.Consultez note technique 35 pour obtenir des conseils sur l'utilisation des fichiers de ressources partagés.
Initialiser la DLL
Comme mentionné ci-dessus, vous devez généralement créer un objet de CDynLinkLibrary pour exporter vos ressources et des classes à l'application cliente.Vous devrez fournir un point d'entrée exporté pour initialiser la DLL.D'une manière minimum, il s'agit d'une routine void qui n'accepte aucun argument et ne retourne aucune valeur, mais vous pouvez spécifier n'importe quel nom.
Chaque application cliente qui souhaite utiliser la DLL doit appeler cette routine d'initialisation, si vous utilisez cette approche.Vous pouvez aussi allouer cet objet de CDynLinkLibrary dans votre DllMain juste après l'appel AfxInitExtensionModule.
La routine d'initialisation doit créer un objet de CDynLinkLibrary dans le tas de application actuel, câblé jusqu'à vos informations de DLL d'extension.Cette opération peut être effectuée à ce qui suit :
extern "C" extern void WINAPI InitXxxDLL()
{
new CDynLinkLibrary(extensionDLL);
}
Le nom actuel, InitXxxDLL dans cet exemple, peut être quelque chose que vous souhaitez.Il n'a pas besoin d'être extern "C", mais cette opération simplifie la liste d'exportation pour la maintenance de.
[!REMARQUE]
Si vous utilisez la DLL d'extension à partir d'une DLL normale, vous devez exporter cette fonction d'initialisation.Cette fonction doit être appelée à partir de la DLL normale avant d'utiliser toutes les classes ou de ressources de DLL d'extension.
Exporter des entrées
Le moyen simple d'exporter vos classes est d'utiliser __declspec(dllimport) et __declspec(dllexport) sur chaque classe et fonction globale que vous souhaitez à l'exportation.Cela facilite beaucoup, mais est moins efficace que donner à chaque point d'entrée (décrit plus loin) étant donné que vous utilisez moins de contrôle de les fonctions sont exportées et vous ne pouvez pas exporter des fonctions par ordinal.TESTDLL1 et TESTDLL2 utilisent cette méthode pour exporter leurs entrées.
Une méthode plus efficace (et la méthode utilisée par MFCxx.DLL) est d'exporter chaque entrée à la main en nommant chaque entrée dans le fichier .DEF.Comme nous exportons les exportations sélectives de notre DLL (autrement dit, pas la totalité), nous devons choisir les interfaces particulières nous souhaitent à l'exportation.C'est difficile dans la mesure où vous devez spécifier les noms tronqués à l'éditeur de liens sous la forme d'entrées dans le fichier .DEF.N'exportez pas de classe C++ à moins que vous ne deviez réellement avoir un lien symbolique pour lui.
Si vous avez essayé d'exporter des classes C++ avec un fichier .DEF avant, vous pouvez développer un outil pour générer cette liste automatiquement.Cela peut se faire dans un processus de lien en deux étapes.Liez votre DLL une fois sans les exportations, et permettent à l'éditeur de liens pour générer un fichier de .MAP.Le fichier de .MAP peut être utilisé pour générer une liste des fonctions qui doivent être exportées, ce avec réorganiser, il peut être utilisé pour générer vos entrées d'EXPORTATION pour le fichier .DEF.La liste d'exportation pour MFCxx.DLL et OLE et les DLL d'extension de base de données, plusieurs milliers en nombre, a été générée avec ce processus (même si ce n'est pas entièrement automatique et ne requiert pas une certaine main ajustant occasionnellement).
CWinApp et.CDynLinkLibrary
Une DLL d'extension MFC n'a pas CWinAppobjet dérivé de sa propre ; à la place il doit utiliser CWinAppobjet dérivé de l'application cliente.Cela signifie que l'application cliente possède la pompe de messages principale, la boucle inactive et ainsi de suite.
Si la DLL d'extension MFC doit gérer des données supplémentaires pour chaque application, vous pouvez dériver une nouvelle classe de CDynLinkLibrary et le créer dans la routine d'InitXxxDLL décrivez ci-dessus.Au moment de l'exécution, la DLL peut consulter la liste des objets CDynLinkLibrary de l'application en cours pour rechercher celui qui correspond à cette DLL d'extension particulière.
Utilisation des ressources dans votre implémentation de DLL
Comme mentionné ci-dessus, la charge de ressources par défaut marchera la liste des objets de CDynLinkLibrary à la recherche du premier fichier EXE ou DLL qui a la ressource demandée.Toutes les API ainsi que toutes les utilisations AfxFindResourceHandle MFC de code interne de parcourir la liste des ressources pour rechercher une ressource, n'importe laquelle elle peut résider.
Si vous souhaitez uniquement aux ressources en charge d'un emplacement spécifique, utilisez les API AfxGetResourceHandle et AfxSetResourceHandle pour enregistrer l'ancien handle et pour définir le nouveau handle.Veillez à restaurer l'ancien handle de la ressource avant de retourner à l'application cliente.l'exemple TESTDLL2 utilise cette approche pour charger explicitement un menu.
Le fait de parcourir la liste a pour inconvénient de ralentir les opérations et d'exiger la gestion de plages d'ID de ressources.L'avantage c'est qu'une application cliente qui lie à plusieurs DLL d'extension peut utiliser les ressources fournies par la DLL sans devoir spécifier le handle d'instance de DLL.AfxFindResourceHandle est une API utilisée pour accompagner la liste des ressources pour rechercher une correspondance donnée.Elle prend le nom et le type d'une ressource et retourne le handle de la ressource où elle a été trouvée en premier lieu (ou la valeur NULL).
L'écriture d'une application qui utilise la version de DLL
Spécifications de votre application
Une application qui utilise la version partagée des MFC doit respecter certaines règles simples :
Il doit posséder un objet d' CWinApp et suivre les règles standard pour une pompe de messages.
Il doit être compilé avec un jeu de balises requises de compilateur (voir ci-dessous).
Il doit établir une liaison avec les bibliothèques d'importation de MFCxx.En définissant les indicateurs requis du compilateur, les en-têtes MFC déterminent au moment de la liaison à laquelle la bibliothèque l'application doit être liée.
Pour exécuter le fichier exécutable, MFCxx.DLL doit se trouver dans le chemin d'accès ou le répertoire système de Windows.
La génération 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 avez une demande fonctionnante correctement MFC liée à NAFXCWD.LIB (de débogage) et NAFXCW.LIB (pour la version commerciale) et que vous souhaitez que la convertir pour utiliser la version partagée de la bibliothèque MFC.Vous exécutez l'environnement Visual C++ et qui dispose d'un fichier projet interne.
- Dans le menu de Projets , cliquez sur Propriétés.Dans la page de Général sous Paramètres par défaut du projet, affectez (Microsoft Foundation Classes) à Use MFC in a Shared DLL (MFCxx (d) .dll).
La génération avec NMAKE
Si vous utilisez des fonctionnalités externe d'un makefile Visual C++, soit utiliser NMAKE directement, vous devrez modifier votre makefile pour prendre en charge des options du compilateur et de l'éditeur de liens
Indicateurs requis du compilateur :
- /D_AFXDLL /MD
/D_AFXDLL
Les en-têtes MFC standard ont besoin de ce symbole pour être définie :
- /MD
L'application doit utiliser la version DLL de la bibliothèque Runtime C
Toutes les autres indicateurs de compilateur suivent les valeurs par défaut MFC (par exemple, _DEBUG pour le débogage).
Modifiez la liste de l'éditeur de liens de bibliothèques.Modifiez NAFXCWD.LIB à MFCxxD.LIB et modifiez NAFXCW.LIB à MFCxx.LIB.Remplacez LIBC.LIB par MSVCRT.LIB.Comme pour toute autre bibliothèque MFC il est important que MFCxxD.LIB soit before placé toutes les bibliothèques d'exécution c.
Éventuellement ajoutez /D_AFXDLL à votre version commerciale et déboguer des options du compilateur de ressources (celle qui compilent réellement les ressources avec /R).Cela rend votre plus petit exécutable final en partageant les ressources qui sont présentes dans les DLL MFC.
Une régénération complète est requise une fois ces modifications ont été apportées.
Générer des exemples
La plupart des exemples de programmes MFC peuvent être générées de Visual C++ ou d'un MAKEFILE NMAKE-compatible partagé de la ligne de commande.
Pour convertir l'un de ces exemples pour utiliser MFCxx.DLL, vous pouvez charger le fichier de .MAK dans Visual C++ et définir les options de projet comme décrit ci-dessus.Si vous utilisez la génération de NMAKE, vous pouvez spécifier « AFXDLL=1 » sur la ligne de commande NMAKE et qui génère l'exemple utilisation des bibliothèques MFC partagées.
L'exemple DLLHUSK de concepts avancés par MFC est généré avec la version DLL de MFC.Cet exemple affiche non seulement comment générer une application liée au MFCxx.DLL, mais il illustre également d'autres fonctionnalités de l'option du package de DLL MFC telles que les DLL d'extension MFC décrits plus loin dans cette note technique.
Empaqueter des remarques
La version commerciale des DLL (.DLL de MFCxx U []) sont librement redistribuable.La version debug des DLL ne sont pas librement redistribuable et doivent être utilisées uniquement pendant le développement de votre application.
Les DLL de débogage sont instrumentés d'informations de débogage.À l'aide de le débogueur Visual C++, vous pouvez suivre l'exécution de votre application ainsi que de la DLL.Les DLL de version (.DLL de MFCxx U []) ne contiennent pas d'informations de débogage.
Si vous personnalisez ou régénérez les DLL, vous devez les appeler un élément autre que « MFCxx » que le fichier MFCDLL.MAK MFC SRC décrit les options de génération et contient la logique pour renommer la DLL.Renommer les fichiers est nécessaire, car ces DLL peuvent être partagés par de nombreuses applications MFC.En faisant en sorte que votre version personnalisée de DLL MFC remplacez les installés sur le système peut arrêter une autre application MFC à l'aide de les DLL MFC partagées.
Régénérer les DLL MFC n'est pas recommandé.
Comment MFCxx.DLL occupe implémenté
La section suivante décrit comment la DLL MFC (MFCxx.DLL et MFCxxD.DLL) est implémenté.Présentation des détails ici ne sont pas non plus importants si vous souhaitez uniquement 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.
Présentation d'implémentation
La DLL MFC est vraiment un cas particulier d'une DLL d'extension MFC comme décrit ci-dessus.Elle comporte un grand nombre d'exportations pour un grand nombre de classes.Il existe plusieurs actions supplémentaires que nous faisons dans la DLL MFC qui en font bien plus spéciale qu'une DLL normal d'extension.
Win32 exécute la plupart du travail
La version 16 bits de MFC nécessaire un certain nombre de techniques spéciales y compris les données de par-Applications dans le segment de pile, les segments spéciaux créés par le code de l'assembly du 80x86, les contextes d'exception de processus, et d'autres techniques.Win32 prend en charge directement les données processus par processus dans une DLL, ce que vous souhaitez le plus souvent.Pour la plupart MFCxx.DLL est simplement NAFXCW.LIB empaqueté dans une DLL.Si vous examinez le code source MFC, vous trouverez très peu le _AFXDLL #ifdef, car il existe très peu de cas spéciaux qui doivent être créés.Les cas spéciaux qui sont présents sont spécifiquement pour traiter Win32 sous Windows 3.1 (sinon appelé Win32s).Win32s ne prend pas en charge les données de DLL par processus directement donc la DLL MFC doit utiliser les API Win32 (TLS) de stockage local des threads pour obtenir les données locales de processus.
Impact sur les sources de bibliothèque, fichiers supplémentaires
L'impact de la version de _AFXDLL sur les sources et les en-têtes normales de bibliothèque de classes MFC est relativement mineure.Il existe un fichier de version personnalisée (AFXV_DLL.H) ainsi qu'un fichier d'en-tête supplémentaire (AFXDLL_.H) inclus par l'en-tête clé d'AFXWIN.H.L'en-tête d'AFXDLL_.H inclut la classe de CDynLinkLibrary et d'autres détails d'implémentation d'applications de _AFXDLL et de la DLL d'extension MFC.L'en-tête d'AFXDLLX.H est fourni pour générer des DLL d'extension MFC (voir ci-dessus pour plus d'informations).
Les sources normales à la bibliothèque MFC dans MFC SRC ont quelque le code conditionnel supplémentaire sous le #ifdef de _AFXDLL .Un fichier source supplémentaire (DLLINIT.CPP) contient le code d'initialisation de la DLL d'un supplémentaire et d'autres colle de la version partagée des MFC.
Pour générer la version partagée de MFC, les fichiers supplémentaires fournis.(Voir ci-dessous pour savoir comment générer la DLL.)
Deux fichiers .DEF sont utilisés pour exporter des points d'entrée DLL MFC pour le débogage (MFCxxD.DEF) et libèrent les versions (MFCxx.DEF) de la DLL.
Un fichier .RC (MFCDLL.RC) contient toutes les ressources MFC standard et une ressource VERSIONINFO de la DLL.
Un fichier de .CLW (MFCDLL.CLW) est fourni pour permettre parcourir les classes MFC à l'aide de l'assistant classe.Remarque : cette fonctionnalité n'est pas spécifique à la version DLL MFC.
Gestion de la mémoire
Une application à l'aide de MFCxx.DLL utilise un allocateur de mémoire commun fourni par MSVCRTxx.DLL, la DLL partagées d'exécution c.L'application, les DLL d'extension, et bon en tant que DLL MFC eux-mêmes 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 qui est ultérieurement l'application libère ou vice versa.Étant donné que l'application et la DLL doit utiliser le même allocateur, vous ne devez pas substituer C++ operator new global ou operator delete.Les mêmes règles s'appliquent au reste des routines d'allocation de mémoire runtime C (telles qu' malloc, realloc, free, etc.).
Ordinaux et de classe __declspec (dllexport) et nommer de DLL
Nous n'utilisons pas la fonctionnalité d' class**__declspec(dllexport)** du compilateur C++.À la place, une liste d'exportations est fournie avec les sources de bibliothèque de classes (MFCxx.DEF et MFCxxD.DEF).Uniquement ces sélectionnez l'ensemble de points d'entrée (les fonctions et des données) sont exportés.D'autres symboles, tels que des fonctions privées ou des classes d'implémentation MFC, ne sont pas exportés toutes les exportations sont apportées par ordinal sans nom de chaîne dans le résident ou le tableau non-résident de nom.
À l'aide de class**__declspec(dllexport)** peut être une alternative valide pour la génération de plus petits DLL, mais dans le cas d'un grand DLL comme MFC, le mécanisme exportation par défaut a des limites d'efficacité et de capacité.
Ce qu'il tout le signifie que vous pouvez empaqueter un grand nombre de fonctionnalités dans la version finale MFCxx.DLL qui est uniquement environ 800 Ko sans compromettre beaucoup d'exécution ou charger la vitesse.MFCxx.DLL aurait été ko plus grand cette technique n'avait pas été utilisé.Cela permet également d'ajouter des points d'entrée supplémentaires à la fin de le fichier .DEF pour permettre le versioning simple sans compromettre l'efficacité de rapidité et de taille d'exportation par ordinal.Les révisions de version principale à la bibliothèque de classes MFC modifieront le nom de la bibliothèque.Autrement dit, MFC30.DLL est la DLL redistribuables contenant la version 3,0 de la bibliothèque de classes MFC.Une mise à niveau de cette DLL par exemple dans les MFC hypothétique 3,1, la DLL est nommée MFC31.DLL à la place.De même, 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 un sans « MFC » dans le nom).