TN033: DLL-Version der MFC
In diesem Hinweis wird beschrieben, wie Sie die MFCxx.DLL
MFCxxD.DLL
freigegebenen Dynamischen Linkbibliotheken mit MFC-Anwendungen und MFC-Erweiterungs-DLLs (wobei xx die MFC-Versionsnummer ist) verwenden können. Weitere Informationen zu regulären MFC-DLLs finden Sie unter Verwenden von MFC als Teil einer DLL.
Dieser technische Hinweis behandelt drei Aspekte von DLLs. Die beiden letzten sind für die erweiterten Benutzer gedacht:
Erstellen einer MFC-Anwendung, die die DLL-Version von MFC verwendet
Implementierung der gemeinsam genutzten MFC-Bibliotheken für dynamische Verknüpfungen
Wenn Sie daran interessiert sind, eine DLL mit MFC zu erstellen, die mit Nicht-MFC-Anwendungen (als normale MFC-DLL bezeichnet) verwendet werden kann, lesen Sie technical Note 11.
Übersicht über MFCxx.DLL-Unterstützung: Terminologie und Dateien
Normale MFC DLL: Sie verwenden eine normale MFC DLL, um eine eigenständige DLL mit einigen der MFC-Klassen zu erstellen. Schnittstellen über die App-/DLL-Grenze hinweg sind "C"-Schnittstellen, und die Clientanwendung muss keine MFC-Anwendung sein.
Reguläre MFC-DLLs sind die Version von DLLs, die in MFC 1.0 unterstützt werden. Sie werden in Technical Note 11 und im MFC Advanced Concepts-Beispiel DLLScreenCap
beschrieben.
Hinweis
Ab Visual C++ Version 4.0 ist der Begriff USRDLL veraltet und wurde durch eine reguläre MFC-DLL ersetzt, die statisch mit MFC verknüpft ist. Sie können auch eine normale MFC-DLL erstellen, die dynamisch mit MFC verknüpft.
MFC 3.0 (und höher) unterstützt reguläre MFC-DLLs mit allen neuen Funktionen, einschließlich der OLE- und Datenbankklassen.
AFXDLL: Wird auch als freigegebene Version der MFC-Bibliotheken bezeichnet. Es ist die neue DLL-Unterstützung, die in MFC 2.0 hinzugefügt wurde. Die MFC-Bibliothek selbst befindet sich in einer Reihe von DLLs (siehe unten). Eine Clientanwendung oder DLL verknüpft die benötigten DLLs dynamisch. Schnittstellen über die Anwendungs-/DLL-Grenze hinweg sind C++/MFC-Klassenschnittstellen. Die Clientanwendung MUSS eine MFC-Anwendung sein. Diese DLL unterstützt alle MFC 3.0-Funktionen (Ausnahme: UNICODE wird für die Datenbankklassen nicht unterstützt).
Hinweis
Ab Visual C++ Version 4.0 wird dieser DLL-Typ als "Erweiterungs-DLL" bezeichnet.
Dieser Hinweis wird verwendet MFCxx.DLL
, um auf den gesamten MFC DLL-Satz zu verweisen, der Folgendes umfasst:
Debuggen:
MFCxxD.DLL
(kombiniert) undMFCSxxD.LIB
(statisch).Release:
MFCxx.DLL
(kombiniert) undMFCSxx.LIB
(statisch).Unicode-Debug:
MFCxxUD.DLL
(kombiniert) undMFCSxxD.LIB
(statisch).Unicode Release:
MFCxxU.DLL
(kombiniert) undMFCSxxU.LIB
(statisch).
Hinweis
Die MFCSxx[U][D].LIB
Bibliotheken werden in Verbindung mit den gemeinsam genutzten MFC-DLLs verwendet. Diese Bibliotheken enthalten Code, der statisch mit der Anwendung oder DLL verknüpft werden muss.
Eine Anwendung verknüpft mit den entsprechenden Importbibliotheken:
Debuggen:
MFCxxD.LIB
Release:
MFCxx.LIB
Unicode-Debug:
MFCxxUD.LIB
Unicode-Version:
MFCxxU.LIB
Eine MFC-Erweiterungs-DLL ist eine DLL , die erweitert MFCxx.DLL
(oder die anderen gemeinsam genutzten MFC-DLLs). Hier beginnt die MFC-Komponentenarchitektur. Wenn Sie eine nützliche Klasse von einer MFC-Klasse ableiten oder ein anderes MFC-ähnliches Toolkit erstellen, können Sie sie in einer DLL platzieren. Ihre DLL verwendet MFCxx.DLL
, wie die ultimative Clientanwendung. Eine MFC-Erweiterungs-DLL ermöglicht wiederverwendbare Blattklassen, wiederverwendbare Basisklassen und wiederverwendbare Ansichts- und Dokumentklassen.
Vor- und Nachteile
Warum sollten Sie die freigegebene Version von MFC verwenden
Die Verwendung der freigegebenen Bibliothek kann zu kleineren Anwendungen führen. (Eine minimale Anwendung, die den größten Teil der MFC-Bibliothek verwendet, ist kleiner als 10K).
Die freigegebene Version von MFC unterstützt MFC-Erweiterungs-DLLs und reguläre MFC-DLLs.
Es ist schneller, eine Anwendung zu erstellen, die die freigegebenen MFC-Bibliotheken als eine statisch verknüpfte MFC-Anwendung verwendet. Das liegt daran, dass es nicht notwendig ist, MFC selbst zu verknüpfen. Dies gilt insbesondere für Builds, bei
DEBUG
denen der Linker die Debuginformationen komprimieren muss. Wenn Ihre Anwendung mit einer DLL verknüpft wird, die bereits die Debuginformationen enthält, gibt es weniger Debuginformationen, um zu komprimieren.
Warum sollten Sie die freigegebene Version von MFC nicht verwenden:
- Für den Versand einer Anwendung, die die freigegebene Bibliothek verwendet, müssen Sie und andere Bibliotheken mit Ihrem Programm versenden
MFCxx.DLL
.MFCxx.DLL
ist frei weiterverteilbar wie viele DLLs, aber Sie müssen die DLL trotzdem in Ihrem SETUP-Programm installieren. Außerdem müssen Sie die anderen weiterverteilbaren Bibliotheken versenden, die sowohl von Ihrem Programm als auch von den MFC-DLLs selbst verwendet werden.
So schreiben Sie eine MFC-Erweiterungs-DLL
Eine MFC-Erweiterungs-DLL ist eine DLL, die Klassen und Funktionen enthält, um die Funktionalität der MFC-Klassen zu erweitern. Eine MFC-Erweiterungs-DLL verwendet die freigegebenen MFC-DLLs auf die gleiche Weise wie eine Anwendung sie verwendet, mit einigen zusätzlichen Überlegungen:
Der Buildprozess ähnelt dem Erstellen einer Anwendung, die die freigegebenen MFC-Bibliotheken mit einigen zusätzlichen Compiler- und Linkeroptionen verwendet.
Eine MFC-Erweiterungs-DLL verfügt nicht über eine
CWinApp
abgeleitete Klasse.Eine MFC-Erweiterungs-DLL muss eine spezielle
DllMain
. AppWizard stellt eineDllMain
Funktion zur Auswahl, die Sie ändern können.Eine MFC-Erweiterungs-DLL stellt normalerweise eine Initialisierungsroutine zum Erstellen einer
CDynLinkLibrary
, wenn die MFC-Erweiterungs-DLL Typen oder Ressourcen in die Anwendung exportiertCRuntimeClass
. Eine abgeleitete Klasse vonCDynLinkLibrary
kann verwendet werden, wenn Daten pro Anwendung von der MFC-Erweiterungs-DLL Standard enthalten sein müssen.
Diese Überlegungen werden unten ausführlicher beschrieben. Weitere Informationen finden Sie im MFC Advanced Concepts-Beispiel DLLHUSK
. Es zeigt Folgendes:
Erstellen Sie eine Anwendung mit den freigegebenen Bibliotheken. (
DLLHUSK.EXE
ist eine MFC-Anwendung, die dynamisch mit den MFC-Bibliotheken und anderen DLLs verknüpft.)Erstellen Sie eine MFC-Erweiterungs-DLL. (Es zeigt, wie spezielle Flags wie
_AFXEXT
beim Erstellen einer MFC-Erweiterungs-DLL verwendet werden.)Erstellen Sie zwei Beispiele für MFC-Erweiterungs-DLLs. Eine zeigt die grundlegende Struktur einer MFC-Erweiterungs-DLL mit eingeschränkten Exporten (TESTDLL1) und die andere zeigt den Export einer gesamten Klassenschnittstelle (TESTDLL2).
Sowohl die Clientanwendung als auch alle MFC-Erweiterungs-DLLs müssen dieselbe Version von MFCxx.DLL
. Befolgen Sie die Konventionen der MFC-DLLs, und stellen Sie sowohl eine Debug- als auch eine Releaseversion (/release
) Ihrer MFC-Erweiterungs-DLL bereit. In dieser Praxis können Clientprogramme sowohl Debug- als auch Releaseversionen ihrer Anwendungen erstellen und mit der entsprechenden Debug- oder Releaseversion aller DLLs verknüpfen.
Hinweis
Da C++-Namens-Mangling- und Exportprobleme auftreten, kann sich die Exportliste aus einer MFC-Erweiterungs-DLL zwischen den Debug- und Releaseversionen derselben DLL und DLLs für verschiedene Plattformen unterscheiden. Die Version MFCxx.DLL
verfügt über ca. 2000 exportierte Einstiegspunkte. Der Debug MFCxxD.DLL
verfügt über ca. 3000 exportierte Einstiegspunkte.
Schnelle Notiz zur Speicherverwaltung
Der Abschnitt mit dem Titel "Speicherverwaltung" am Ende dieses technischen Hinweises beschreibt die Implementierung der MFCxx.DLL
mit der freigegebenen Version von MFC. Die Informationen, die Sie wissen müssen, um nur eine MFC-Erweiterungs-DLL zu implementieren, wird hier beschrieben.
MFCxx.DLL
und alle in den Adressraum einer Clientanwendung geladenen MFC-Erweiterungs-DLLs verwenden denselben Speicherzuordnungs-, Ressourcenlade- und anderen "globalen" MFC-Status, als wären sie in derselben Anwendung. Es ist wichtig, da die nicht-MFC-DLL-Bibliotheken und reguläreN MFC-DLLs, die statisch mit MFC verknüpft sind, das genaue Gegenteil tun: Jede DLL weist aus ihrem eigenen Speicherpool zu.
Wenn eine MFC-Erweiterungs-DLL Speicher zuweist, kann dieser Speicher frei mit jedem anderen anwendungsgeteilten Objekt vermischt werden. Wenn eine Anwendung, die die freigegebenen MFC-Bibliotheken verwendet, abstürzt, Standard das Betriebssystem die Integrität einer anderen MFC-Anwendung, die die DLL gemeinsam verwendet, enthält.
Ebenso werden andere "globale" MFC-Zustände wie die aktuelle ausführbare Datei zum Laden von Ressourcen ebenfalls zwischen der Clientanwendung, allen MFC-Erweiterungs-DLLs und MFCxx.DLL
sich selbst gemeinsam genutzt.
Erstellen einer MFC-Erweiterungs-DLL
Sie können AppWizard verwenden, um ein MFC-Erweiterungs-DLL-Projekt zu erstellen, und es generiert automatisch die entsprechenden Compiler- und Linkereinstellungen. Außerdem wird eine DllMain
Funktion generiert, die Sie ändern können.
Wenn Sie ein vorhandenes Projekt in eine MFC-Erweiterungs-DLL konvertieren, beginnen Sie mit den Standardeinstellungen, die mit der freigegebenen Version von MFC erstellt werden. Nehmen Sie dann folgende Änderungen vor:
Fügen Sie die Compilerkennzeichnungen hinzu
/D_AFXEXT
. Wählen Sie im Dialogfeld "Projekteigenschaften" die Kategorie "C/C++>-Vorprozessor" aus. Fügen Sie_AFXEXT
zum Feld "Makros definieren" hinzu, wobei jedes Element durch Semikolons getrennt wird.Entfernen Sie den
/Gy
Compilerschalter. Wählen Sie im Dialogfeld "Projekteigenschaften" die Kategorie "C/C++>-Codegenerierung" aus. Stellen Sie sicher, dass die Funktionsebenenverknüpfungseigenschaft nicht aktiviert ist. Diese Einstellung erleichtert das Exportieren von Klassen, da der Linker keine nicht referenzierten Funktionen entfernt. Wenn das ursprüngliche Projekt eine reguläre MFC-DLL erstellt hat, die statisch mit MFC verknüpft ist, ändern Sie die/MT
Compileroption (oder/MTd
) in/MD
(oder/MDd
).Erstellen Sie eine Exportbibliothek mit der
/DLL
Option "LINK". Diese Option wird festgelegt, wenn Sie ein neues Ziel erstellen und win32 Dynamic Link Library als Zieltyp angeben.
Ändern der Kopfzeilendateien
Das übliche Ziel einer MFC-Erweiterungs-DLL besteht darin, einige allgemeine Funktionen in eine oder mehrere Anwendungen zu exportieren, die diese Funktionalität verwenden können. Im Wesentlichen exportiert die DLL Klassen und globale Funktionen für die Verwendung durch Ihre Clientanwendungen.
Um sicherzustellen, dass jede Memberfunktion für den Import oder Export entsprechend gekennzeichnet wird, verwenden Sie die speziellen Deklarationen __declspec(dllexport)
und __declspec(dllimport)
. Wenn Clientanwendungen Ihre Klassen verwenden, möchten Sie, dass sie als __declspec(dllimport)
deklariert werden. Wenn die MFC-Erweiterungs-DLL selbst erstellt wird, sollten die Funktionen als __declspec(dllexport)
deklariert werden. Die integrierte DLL muss auch die Funktionen exportieren, damit die Clientprogramme zur Ladezeit an sie binden können.
Verwenden Sie AFX_EXT_CLASS
zum Exportieren der gesamten Klasse in der Klassendefinition. Das Framework definiert dieses Makro als __declspec(dllexport)
wann _AFXDLL
und _AFXEXT
wird definiert, definiert es jedoch als __declspec(dllimport)
wann _AFXEXT
nicht definiert. _AFXEXT
wird nur beim Erstellen der MFC-Erweiterungs-DLL definiert. Beispiel:
class AFX_EXT_CLASS CExampleExport : public CObject
{ /* ... class definition ... */ };
Exportieren von Teilen einer Klasse
Manchmal möchten Sie vielleicht nur die einzelnen erforderlichen Mitglieder Ihres Kurses exportieren. Wenn Sie beispielsweise eine CDialog
abgeleitete Klasse exportieren, müssen Sie möglicherweise nur den Konstruktor und den DoModal
Aufruf exportieren. Sie können diese Member mithilfe der DEF-Datei der DLL exportieren, aber Sie können auch auf die gleiche Weise für die einzelnen Member verwenden AFX_EXT_CLASS
, die Sie exportieren müssen.
Beispiel:
class CExampleDialog : public CDialog
{
public:
AFX_EXT_CLASS CExampleDialog();
AFX_EXT_CLASS int DoModal();
// rest of class definition
// ...
};
Wenn Sie dies tun, tritt möglicherweise ein zusätzliches Problem auf, da Sie nicht alle Member der Klasse exportieren. Das Problem besteht darin, dass MFC-Makros funktionieren. Mehrere MFC-Hilfsmakros deklarieren oder definieren Datenmember. Ihre DLL muss diese Datenmmber ebenfalls exportieren.
Beispielsweise wird das DECLARE_DYNAMIC Makro beim Erstellen einer MFC-Erweiterungs-DLL wie folgt definiert:
#define DECLARE_DYNAMIC(class_name) \
protected: \
static CRuntimeClass* PASCAL _GetBaseClass(); \
public: \
static AFX_DATA CRuntimeClass class##class_name; \
virtual CRuntimeClass* GetRuntimeClass() const; \
Die Zeile, die beginnt, deklariert ein statisches static AFX_DATA
Objekt innerhalb der Klasse. Um diese Klasse ordnungsgemäß zu exportieren und auf die Laufzeitinformationen von einer Client-EXE zuzugreifen, müssen Sie dieses statische Objekt exportieren. Da das statische Objekt mit dem Modifizierer AFX_DATA
deklariert wird, müssen Sie nur beim Erstellen der DLL definieren AFX_DATA
__declspec(dllexport)
. Definieren Sie sie so, als __declspec(dllimport)
ob Sie die ausführbare Clientdatei erstellen.
Wie oben beschrieben, AFX_EXT_CLASS
ist bereits auf diese Weise definiert. Sie müssen lediglich neu definieren AFX_DATA
, damit sie mit AFX_EXT_CLASS
ihrer Klassendefinition identisch sind.
Beispiel:
#undef AFX_DATA
#define AFX_DATA AFX_EXT_CLASS
class CExampleView : public CView
{
DECLARE_DYNAMIC()
// ... class definition ...
};
#undef AFX_DATA
#define AFX_DATA
MFC verwendet immer das AFX_DATA
Symbol für Datenelemente, die es in seinen Makros definiert, sodass diese Technik für alle solchen Szenarien funktioniert. Beispielsweise funktioniert es für DECLARE_MESSAGE_MAP.
Hinweis
Wenn Sie die gesamte Klasse anstelle ausgewählter Member der Klasse exportieren, werden statische Datenmember automatisch exportiert.
Sie können dieselbe Technik verwenden, um den CArchive
Extraktionsoperator automatisch für Klassen zu exportieren, die die DECLARE_SERIAL und IMPLEMENT_SERIAL Makros verwenden. Exportieren Sie den Archivoperator, indem Sie die Klassendeklarationen (in der Headerdatei) mit dem folgenden Code in Klammern klammern:
#undef AFX_API
#define AFX_API AFX_EXT_CLASS
/* your class declarations here */
#undef AFX_API
#define AFX_API
Einschränkungen für _AFXEXT
Sie können das _AFXEXT
Präprozessorsymbol für Ihre MFC-Erweiterungs-DLLs verwenden, solange Sie nicht über mehrere Ebenen von MFC-Erweiterungs-DLLs verfügen. Wenn Sie über MFC-Erweiterungs-DLLs verfügen, die Klassen aus Ihren eigenen MFC-Erweiterungs-DLLs aufrufen oder davon abgeleitet sind, und diese wiederum von den MFC-Klassen abgeleitet sind, müssen Sie ein eigenes Präprozessorsymbol verwenden, um Mehrdeutigkeit zu vermeiden.
Das Problem besteht darin, dass Sie in Win32 alle Daten explizit deklarieren müssen, um __declspec(dllexport)
sie aus einer DLL zu exportieren und __declspec(dllimport)
aus einer DLL zu importieren. Wenn Sie definieren _AFXEXT
, stellen die MFC-Header sicher, dass diese AFX_EXT_CLASS
ordnungsgemäß definiert sind.
Wenn Sie über mehrere Ebenen verfügen, reicht ein Symbol, z AFX_EXT_CLASS
. B. nicht aus: Eine MFC-Erweiterungs-DLL kann ihre eigenen Klassen exportieren und auch andere Klassen aus einer anderen MFC-Erweiterungs-DLL importieren. Verwenden Sie zum Behandeln dieses Problems ein spezielles Präprozessorsymbol, das angibt, dass Sie die DLL selbst erstellen, anstatt die DLL zu verwenden. Stellen Sie sich beispielsweise zwei MFC-Erweiterungs-DLLs und A.DLL
.B.DLL
Sie exportieren jeweils einige Klassen in A.H
bzw B.H
. B.DLL
verwendet die Klassen von A.DLL
. Die Headerdateien würden in etwa wie folgt aussehen:
/* 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 ... */ };
Wenn A.DLL
sie erstellt wird, wird sie mit /DA_IMPL
und wann B.DLL
erstellt wird, mit dieser erstellt /DB_IMPL
. Durch die Verwendung separater Symbole für jede DLL CExampleB
wird exportiert und CExampleA
beim Erstellen B.DLL
importiert. CExampleA
wird beim Erstellen A.DLL
und Importieren beim Verwenden von B.DLL
oder einem anderen Client exportiert.
Dieser Layertyp kann nicht ausgeführt werden, wenn die integrierten AFX_EXT_CLASS
Und _AFXEXT
Präprozessorsymbole verwendet werden. Die oben beschriebene Technik löst dieses Problem auf die gleiche Weise wie MFC. MFC verwendet diese Technik beim Erstellen seiner OLE-, Datenbank- und Netzwerk-MFC-Erweiterungs-DLLs.
Die gesamte Klasse wird trotzdem nicht exportiert.
Auch hier müssen Sie sich besonders kümmern, wenn Sie keine ganze Klasse exportieren. Stellen Sie sicher, dass die erforderlichen Datenelemente, die von den MFC-Makros erstellt wurden, ordnungsgemäß exportiert werden. Sie können dies tun, indem Sie das Makro Ihrer spezifischen Klasse neu definieren AFX_DATA
. Definieren Sie sie neu, wenn Sie die gesamte Klasse nicht exportieren.
Beispiel:
// 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
Hier sehen Sie den Code, den Sie in Ihrer Standard Quelldatei für Ihre MFC-Erweiterungs-DLL platzieren sollten. Es sollte nach dem Standard enthalten sein. Wenn Sie AppWizard zum Erstellen von Startdateien für eine MFC-Erweiterungs-DLL verwenden, stellt sie eine DllMain
für Sie bereit.
#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
}
Der Aufruf zum AfxInitExtensionModule
Erfassen der Laufzeitklassen (CRuntimeClass
Strukturen) des Moduls und seiner Objektfabriken (COleObjectFactory
Objekte) zur späteren Verwendung beim Erstellen des CDynLinkLibrary
Objekts. Mit dem (optionalen) Aufruf AfxTermExtensionModule
kann MFC die MFC-Erweiterungs-DLL sauber, wenn jeder Prozess getrennt wird (was geschieht, wenn der Prozess beendet wird oder wenn die DLL von einem FreeLibrary
Aufruf entladen wird) von der MFC-Erweiterungs-DLL. Da die meisten MFC-Erweiterungs-DLLs nicht dynamisch geladen werden (normalerweise werden sie über ihre Importbibliotheken verknüpft), ist der Aufruf normalerweise AfxTermExtensionModule
nicht erforderlich.
Wenn Ihre Anwendung MFC-Erweiterungs-DLLs dynamisch lädt und frei gibt, stellen Sie sicher, dass Sie wie oben gezeigt aufrufen AfxTermExtensionModule
. Achten Sie außerdem darauf, (anstelle von Win32-Funktionen LoadLibrary
und FreeLibrary
) zu verwendenAfxLoadLibrary
, AfxFreeLibrary
wenn Ihre Anwendung mehrere Threads verwendet oder eine MFC-Erweiterungs-DLL dynamisch lädt. Die Verwendung AfxLoadLibrary
und AfxFreeLibrary
stellt sicher, dass der Start- und Herunterfahren-Code, der ausgeführt wird, wenn die MFC-Erweiterungs-DLL geladen und entladen wird, den globalen MFC-Zustand nicht beschädigt.
Die Headerdatei AFXDLLX.H
enthält spezielle Definitionen für Strukturen, die in MFC-Erweiterungs-DLLs verwendet werden, z. B. die Definition für AFX_EXTENSION_MODULE
und CDynLinkLibrary
.
Die globale ErweiterungDLL muss wie dargestellt deklariert werden. Im Gegensatz zur 16-Bit-Version von MFC können Sie während dieser Zeit Arbeitsspeicher zuweisen und MFC-Funktionen aufrufen, da die MFCxx.DLL
Initialisierung nach dem Aufruf DllMain
vollständig erfolgt.
Gemeinsame Nutzung von Ressourcen und Klassen
Einfache MFC-Erweiterungs-DLLs benötigen nur einige Wenige Funktionen mit geringer Bandbreite in die Clientanwendung und nichts mehr. Mehr benutzeroberflächenintensive DLLs möchten möglicherweise Ressourcen und C++-Klassen in die Clientanwendung exportieren.
Der Export von Ressourcen erfolgt über eine Ressourcenliste. In jeder Anwendung handelt es sich um eine einzeln verknüpfte Liste von CDynLinkLibrary
Objekten. Bei der Suche nach einer Ressource sehen sich die meisten der standardmäßigen MFC-Implementierungen, die Ressourcen laden, zuerst im aktuellen Ressourcenmodul (AfxGetResourceHandle
) an und wenn sie nicht gefunden wurde, führen Sie die Liste der CDynLinkLibrary
Objekte aus, die versuchen, die angeforderte Ressource zu laden.
Die dynamische Erstellung von C++-Objekten mit einem C++-Klassennamen ist ähnlich. Der MFC-Objekt deserialisierungsmechanismus muss alle CRuntimeClass
Objekte registriert haben, damit es rekonstruieren kann, indem das C++-Objekt des erforderlichen Typs basierend auf dem zuvor gespeicherten Typ dynamisch erstellt wird.
Wenn die Clientanwendung Klassen in Ihrer MFC-Erweiterungs-DLL DECLARE_SERIAL
verwenden soll, müssen Sie Ihre Klassen exportieren, um sie für die Clientanwendung sichtbar zu machen. Sie erfolgt auch durch Durchlaufen der CDynLinkLibrary
Liste.
Im MFC Advanced Concepts-Beispiel DLLHUSK
sieht die Liste ungefähr wie folgt aus:
head -> DLLHUSK.EXE - or - DLLHUSK.EXE
| |
TESTDLL2.DLL TESTDLL2.DLL
| |
TESTDLL1.DLL TESTDLL1.DLL
| |
| |
MFC90D.DLL MFC90.DLL
Der MFCxx.DLL
Eintrag ist in der Regel letzte in der Ressourcen- und Klassenliste. MFCxx.DLL
enthält alle standardmäßigen MFC-Ressourcen, einschließlich Aufforderungszeichenfolgen für alle Standardbefehls-IDs. Wenn Sie sie am Ende der Liste platzieren, können sowohl DLLs als auch die Clientanwendung selbst auf die freigegebenen Ressourcen in der MFCxx.DLL
Liste angewiesen werden, anstatt eigene Kopien zu haben.
Das Zusammenführen der Ressourcen und Klassennamen aller DLLs in den Namensraum der Clientanwendung hat den Nachteil, dass Sie darauf achten müssen, welche IDs oder Namen Sie auswählen. Sie können dieses Feature deaktivieren, indem Sie weder Ihre Ressourcen noch ein CDynLinkLibrary
Objekt in die Clientanwendung exportieren. Im DLLHUSK
Beispiel wird der Freigegebene Ressourcenname mithilfe mehrerer Headerdateien verwaltet. Weitere Tipps zur Verwendung freigegebener Ressourcendateien finden Sie in technischem Hinweis 35 .
Initialisieren der DLL
Wie Erwähnung oben beschrieben, möchten Sie in der Regel ein CDynLinkLibrary
Objekt erstellen, um Ihre Ressourcen und Klassen in die Clientanwendung zu exportieren. Sie müssen einen exportierten Einstiegspunkt bereitstellen, um die DLL zu initialisieren. Minimal ist es eine void
Routine, die keine Argumente akzeptiert und nichts zurückgibt, aber es kann alles sein, was Ihnen gefällt.
Jede Clientanwendung, die Ihre DLL verwenden möchte, muss diese Initialisierungsroutine aufrufen, wenn Sie diesen Ansatz verwenden. Sie können dieses CDynLinkLibrary
Objekt auch direkt nach dem Aufruf in Ihrem DllMain
Objekt AfxInitExtensionModule
zuordnen.
Die Initialisierungsroutine muss ein CDynLinkLibrary
Objekt im Heap der aktuellen Anwendung erstellen, verkabelt mit den MFC-Erweiterungs-DLL-Informationen. Sie können dies tun, indem Sie eine Funktion wie diese definieren:
extern "C" extern void WINAPI InitXxxDLL()
{
new CDynLinkLibrary(extensionDLL);
}
Der Routinename InitXxxDLL in diesem Beispiel kann beliebig sein. Es muss nicht seinextern "C"
, aber es erleichtert die Exportliste Standard tain.
Hinweis
Wenn Sie Ihre MFC-Erweiterungs-DLL aus einer regulären MFC-DLL verwenden, müssen Sie diese Initialisierungsfunktion exportieren. Diese Funktion muss von der regulären MFC-DLL aufgerufen werden, bevor Sie MFC-Erweiterungs-DLL-Klassen oder -Ressourcen verwenden.
Exportieren von Einträgen
Die einfache Möglichkeit zum Exportieren Ihrer Klassen besteht darin, jede Klasse und globale Funktion zu verwenden __declspec(dllimport)
__declspec(dllexport)
, die Sie exportieren möchten. Es ist viel einfacher, aber es ist weniger effizient, als jeden Einstiegspunkt in einer DEF-Datei zu benennen, wie unten beschrieben. Das liegt daran, dass Sie weniger Kontrolle darüber haben, welche Funktionen exportiert werden. Und Sie können die Funktionen nicht nach Ordnungszahl exportieren. TESTDLL1 und TESTDLL2 verwenden diese Methode, um ihre Einträge zu exportieren.
Eine effizientere Methode besteht darin, jeden Eintrag zu exportieren, indem er in der DEF-Datei benannt wird. Diese Methode wird von MFCxx.DLL
. Da wir selektiv aus unserer DLL exportieren, müssen wir entscheiden, welche bestimmten Schnittstellen wir exportieren möchten. Es ist schwierig, da Sie die verworrenen Namen für den Linker in Form von Einträgen in der DEF-Datei angeben müssen. Exportieren Sie keine C++-Klasse, es sei denn, Sie benötigen dafür eine symbolische Verknüpfung.
Wenn Sie zuvor versucht haben, C++-Klassen mit einer DEF-Datei zu exportieren, sollten Sie ein Tool entwickeln, um diese Liste automatisch zu generieren. Dies kann mithilfe eines zweistufigen Verknüpfungsprozesses erfolgen. Verknüpfen Sie die DLL einmal ohne Exporte, und ermöglichen Sie dem Linker, eine MAP-Datei zu generieren. Die MAP-Datei enthält eine Liste der Funktionen, die exportiert werden sollen. Mit einer Neuanordnung können Sie sie verwenden, um die EXPORT-Einträge für Ihre DEF-Datei zu generieren. Die Exportliste für MFCxx.DLL
und die OLE- und Datenbank-MFC-Erweiterungs-DLLs, mehrere Tausend in Der Zahl, wurde mit einem solchen Prozess generiert (obwohl es nicht vollständig automatisch ist und jede Zeit eine Handoptimierung erfordert).
CWinApp vs. CDynLinkLibrary
Eine MFC-Erweiterungs-DLL verfügt nicht über ein CWinApp
eigenes -abgeleitetes Objekt. Stattdessen muss es mit dem CWinApp
-abgeleiteten Objekt der Clientanwendung arbeiten. Dies bedeutet, dass die Clientanwendung die Standard Nachrichtenpumpe, die Leerlaufschleife usw. besitzt.
Wenn Ihre MFC-Erweiterungs-DLL zusätzliche Daten für jede Anwendung Standard muss, können Sie eine neue Klasse ableiten CDynLinkLibrary
und in der InitXxxDLL
oben beschriebenen Routine erstellen. Bei der Ausführung kann die DLL die Liste der CDynLinkLibrary
Objekte der aktuellen Anwendung überprüfen, um das Objekt für diese bestimmte MFC-Erweiterungs-DLL zu finden.
Verwenden von Ressourcen in Ihrer DLL-Implementierung
Wie oben Erwähnung, führt die Standardressourcenlast die Liste der CDynLinkLibrary
Objekte durch, die nach der ersten EXE oder DLL suchen, die über die angeforderte Ressource verfügt. Alle MFC-APIs und der gesamte interne Code werden AfxFindResourceHandle
verwendet, um die Ressourcenliste zu durchlaufen, um eine Ressource zu finden, unabhängig davon, wo sie sich befindet.
Wenn Sie nur Ressourcen von einem bestimmten Ort laden möchten, verwenden Sie die APIs AfxGetResourceHandle
, und AfxSetResourceHandle
speichern Sie das alte Handle, und legen Sie das neue Handle fest. Achten Sie darauf, dass Sie das alte Ressourcenhandle wiederherstellen, bevor Sie zur Clientanwendung zurückkehren. Im Beispiel TESTDLL2 wird dieser Ansatz zum expliziten Laden eines Menüs verwendet.
Das Durchlaufen der Liste hat einige Nachteile: Es ist etwas langsamer und erfordert das Verwalten von Ressourcen-ID-Bereichen. Der Vorteil liegt darin, dass eine Clientanwendung, die mit mehreren MFC-Erweiterungs-DLLs verknüpft ist, jede durch die DLL zur Verfügung gestellte Ressource nutzen kann, ohne dass das DLL-Instanzenhandle angegeben werden muss. AfxFindResourceHandle
ist eine API, die bei der Suche nach einer bestimmten Übereinstimmung die Ressourcenliste durchläuft. Er verwendet den Namen und typ einer Ressource und gibt das Ressourcenhandle zurück, in dem die Ressource zuerst gefunden wird, oder NULL.
Schreiben einer Anwendung, die die DLL-Version verwendet
Anwendungsanforderungen
Eine Anwendung, die die freigegebene Version von MFC verwendet, muss einige grundlegende Regeln befolgen:
Es muss über ein
CWinApp
Objekt verfügen und den Standardregeln für eine Nachrichtenpumpe folgen.Sie muss mit einer Reihe erforderlicher Compilerflags kompiliert werden (siehe unten).
Sie muss eine Verknüpfung mit den MFCxx-Importbibliotheken herstellen. Durch Festlegen der erforderlichen Compilerflags bestimmen die MFC-Header zur Verknüpfungszeit, mit welcher Bibliothek die Anwendung verknüpft werden soll.
Zum Ausführen der ausführbaren Datei
MFCxx.DLL
muss sich der Pfad oder das Windows-Systemverzeichnis befinden.
Erstellen mit der Entwicklungsumgebung
Wenn Sie die interne Makefile mit den meisten Standardstandardeinstellungen verwenden, können Sie das Projekt ganz einfach ändern, um die DLL-Version zu erstellen.
Im folgenden Schritt wird davon ausgegangen, dass Sie eine ordnungsgemäß funktionierende MFC-Anwendung verknüpft haben NAFXCWD.LIB
(zum Debuggen) und (für Release) und NAFXCW.LIB
sie konvertieren möchten, um die freigegebene Version der MFC-Bibliothek zu verwenden. Sie führen die Visual Studio-Umgebung aus und verfügen über eine interne Projektdatei.
- Wählen Sie im Menü "Projekte" die Option "Eigenschaften" aus. Legen Sie auf der Seite "Allgemein " unter "Project Defaults" Microsoft Foundation Classes auf " MFC" in einer freigegebenen DLL (MFCxx(d).dll) fest.
Gebäude mit NMAKE
Wenn Sie das externe Makefile-Feature des Compilers verwenden oder NMAKE direkt verwenden, müssen Sie Ihre Makefile bearbeiten, um die erforderlichen Compiler- und Linkeroptionen zu unterstützen.
Erforderliche Compilerflags:
/D_AFXDLL /MD
/D_AFXDLL
Die MFC-Standardheader müssen das _AFXDLL
Symbol definieren.
/MD
Die Anwendung muss die DLL-Version der C-Laufzeitbibliothek verwenden.
Alle anderen Compilerkennzeichnungen folgen den MFC-Standardwerten (z _DEBUG
. B. zum Debuggen).
Bearbeiten Sie die Linkerliste der Bibliotheken. Ändern Sie NAFXCWD.LIB
in MFCxxD.LIB
und NAFXCW.LIB
in MFCxx.LIB
. Ersetzen Sie LIBC.LIB
durch MSVCRT.LIB
. Wie bei jeder anderen MFC-Bibliothek ist es wichtig, dass vor MFCxxD.LIB
allen C-Runtime-Bibliotheken platziert wird.
Fügen Sie /D_AFXDLL
optional sowohl die Versions- als auch die Debugressourcencompileroptionen hinzu (die, mit /R
der die Ressourcen tatsächlich kompiliert werden). Mit dieser Option wird die endgültige ausführbare Datei verkleinert, indem die Ressourcen freigegeben werden, die in den MFC-DLLs vorhanden sind.
Eine vollständige Neuerstellung ist erforderlich, nachdem diese Änderungen vorgenommen wurden.
Erstellen der Beispiele
Die meisten MFC-Beispielprogramme können aus Visual C++ oder aus einer freigegebenen NMAKE-kompatiblen MAKEFILE-Datei über die Befehlszeile erstellt werden.
Um eines dieser Beispiele zu konvertieren, MFCxx.DLL
können Sie die MAK-Datei in Visual C++ laden und die Project-Optionen wie oben beschrieben festlegen. Wenn Sie den NMAKE-Build verwenden, können Sie in der NMAKE-Befehlszeile angeben AFXDLL=1
und das Beispiel mithilfe der freigegebenen MFC-Bibliotheken erstellen.
Das MFC Advanced Concepts-Beispiel DLLHUSK wird mit der DLL-Version von MFC erstellt. In diesem Beispiel wird nicht nur veranschaulicht, wie eine Anwendung erstellt wird, die MFCxx.DLL
mit verknüpft ist, sondern auch andere Features der MFC DLL-Verpackungsoption, z. B. MFC-Erweiterungs-DLLs, die weiter unten in dieser technischen Anmerkung beschrieben werden.
Packen von Notizen
Die Releaseversionen der DLLs (MFCxx.DLL
und MFCxxU.DLL
) sind frei verteilbar. Die Debugversionen der DLLs sind nicht frei verteilbar und sollten nur während der Entwicklung Ihrer Anwendung verwendet werden.
Die Debug-DLLs werden mit Debuginformationen bereitgestellt. Mithilfe des Visual C++-Debuggers können Sie die Ausführung ihrer Anwendung und der DLL nachverfolgen. Die Release-DLLs (MFCxx.DLL
und MFCxxU.DLL
) enthalten keine Debuginformationen.
Wenn Sie die DLLs anpassen oder neu erstellen, sollten Sie diese etwas anderes als "MFCxx" aufrufen. Die MFC SRC-Datei MFCDLL.MAK
beschreibt Buildoptionen und enthält die Logik zum Umbenennen der DLL. Das Umbenennen der Dateien ist erforderlich, da diese DLLs potenziell von vielen MFC-Anwendungen freigegeben werden. Wenn Ihre benutzerdefinierte Version der MFC-DLLs die auf dem System installierten ersetzen kann, kann eine andere MFC-Anwendung mit den freigegebenen MFC-DLLs abgebrochen werden.
Das Neuerstellen der MFC-DLLs wird nicht empfohlen.
Implementierung der MFCxx.DLL
Im folgenden Abschnitt wird beschrieben, wie die MFC DLL (MFCxx.DLL
und MFCxxD.DLL
) implementiert wird. Wenn Sie nur die MFC-DLL mit Ihrer Anwendung verwenden möchten, ist es nicht wichtig, die Details hier zu verstehen. Die hier aufgeführten Details sind nicht wichtig, um zu verstehen, wie sie eine MFC-Erweiterungs-DLL schreiben, aber das Verständnis dieser Implementierung kann Ihnen helfen, Ihre eigene DLL zu schreiben.
Implementierungsübersicht
Die MFC DLL ist wirklich ein Sonderfall einer MFC-Erweiterungs-DLL wie oben beschrieben. Es verfügt über eine große Anzahl von Exporten für eine große Anzahl von Klassen. Es gibt ein paar zusätzliche Dinge, die wir in der MFC-DLL tun, die es noch spezieller machen als eine normale MFC-Erweiterungs-DLL.
Win32 erledigt den größten Teil der Arbeit
Die 16-Bit-Version von MFC benötigt eine Reihe spezieller Techniken, einschließlich App-Daten im Stapelsegment, spezielle Segmente, die von etwa 80x86-Assemblycode, Prozess-Ausnahmekontexten und anderen Techniken erstellt wurden. Win32 unterstützt daten pro Prozess direkt in einer DLL, was Sie am meisten benötigen. In den meisten Fällen MFCxx.DLL
wird nur NAFXCW.LIB
eine DLL verpackt. Wenn Sie den MFC-Quellcode betrachten, finden Sie nur wenige #ifdef _AFXDLL
Fälle, da es nicht viele Sonderfälle gibt, die erstellt werden müssen. Die Speziellen Fälle, in denen es sich speziell um Win32 unter Windows 3.1 (auch als Win32s bezeichnet) handelt. Win32s unterstützt dll-Daten pro Prozess nicht direkt. Die MFC-DLL muss die Win32-WIN32-APIs (Thread-Local Storage, TLS) verwenden, um lokale Daten zu erhalten.
Auswirkungen auf Bibliotheksquellen, zusätzliche Dateien
Die Auswirkungen der _AFXDLL
Version auf die normalen MFC-Klassenbibliotheksquellen und Header sind relativ gering. Es gibt eine spezielle Versionsdatei (AFXV_DLL.H
) und eine zusätzliche Headerdatei (AFXDLL_.H
) in der Standard-KopfzeileAFXWIN.H
. Der AFXDLL_.H
Header enthält die CDynLinkLibrary
Klasse und andere Implementierungsdetails von _AFXDLL
Anwendungen und MFC-Erweiterungs-DLLs. Der AFXDLLX.H
Header wird für das Erstellen von MFC-Erweiterungs-DLLs bereitgestellt (einzelheiten hierzu finden Sie oben).
Die regulären Quellen in der MFC-Bibliothek in MFC SRC verfügen über zusätzlichen bedingten Code unter dem _AFXDLL
#ifdef. Eine zusätzliche Quelldatei (DLLINIT.CPP
) enthält den zusätzlichen DLL-Initialisierungscode und anderen Klebstoff für die freigegebene Version von MFC.
Um die freigegebene Version von MFC zu erstellen, werden zusätzliche Dateien bereitgestellt. (Ausführliche Informationen zum Erstellen der DLL finden Sie unten.)
Zwei DEF-Dateien werden zum Exportieren der MFC-DLL-Einstiegspunkte für Debugversionen (
MFCxxD.DEF
) und Release (MFCxx.DEF
) der DLL verwendet.Eine RC-Datei (
MFCDLL.RC
) enthält alle standardmäßigen MFC-Ressourcen und eineVERSIONINFO
Ressource für die DLL.Eine CLW-Datei (
MFCDLL.CLW
) wird bereitgestellt, um das Durchsuchen der MFC-Klassen mit ClassWizard zu ermöglichen. Dieses Feature ist nicht speziell für die DLL-Version von MFC.
Speicherverwaltung
Eine Anwendung, MFCxx.DLL
die einen gemeinsamen Speicherzuordnungsmodul verwendet, der von MSVCRTxx.DLL
der freigegebenen C-Runtime-DLL bereitgestellt wird. Die Anwendung, alle MFC-Erweiterungs-DLLs und die MFC-DLLs verwenden diesen gemeinsam genutzten Speicherzuordnungsserver. Mithilfe einer freigegebenen DLL für die Speicherzuweisung können die MFC-DLLs Speicher zuordnen, die später von der Anwendung freigegeben werden oder umgekehrt. Da sowohl die Anwendung als auch die DLL denselben Allocator verwenden müssen, sollten Sie die globale operator new
C++- oder operator delete
C++-Datei nicht außer Kraft setzen. Die gleichen Regeln gelten für die restlichen C-Laufzeitspeicherzuweisungsroutinen (z malloc
. B. , , realloc
, free
und andere).
Ordinale und Klassennamen __declspec(dllexport) und DLL-Benennung
Wir verwenden class
__declspec(dllexport)
die Funktionalität des C++-Compilers nicht. Stattdessen ist eine Liste der Exporte in die Klassenbibliotheksquellen (MFCxx.DEF
und MFCxxD.DEF
) enthalten. Es werden nur eine ausgewählte Gruppe von Einstiegspunkten (Funktionen und Daten) exportiert. Andere Symbole, z. B. private MFC-Implementierungsfunktionen oder Klassen, werden nicht exportiert. Alle Exporte werden von Ordinal ohne Zeichenfolgennamen in der residenten oder nicht-residenten Namenstabelle durchgeführt.
Die Verwendung class
__declspec(dllexport)
kann eine praktikable Alternative zum Erstellen kleinerer DLLs sein, aber in einer großen DLL wie MFC weist der Standardmäßige Exportmechanismus Effizienz- und Kapazitätsgrenzwerte auf.
Was alles bedeutet, ist, dass wir eine große Anzahl von Funktionen in der Version MFCxx.DLL
packen können, die nur etwa 800 KB sind, ohne eine hohe Ausführungs- oder Ladegeschwindigkeit zu beeinträchtigen. MFCxx.DLL
wäre 100 KB größer gewesen, wenn diese Technik nicht verwendet wurde. Die Technik ermöglicht das Hinzufügen zusätzlicher Einstiegspunkte am Ende der DEF-Datei. Es ermöglicht einfache Versionsverwaltung, ohne die Geschwindigkeit und Größe der Exporteffizienz durch Ordinal zu beeinträchtigen. Hauptversionsänderungen in der MFC-Klassenbibliothek ändern den Bibliotheksnamen. Das heißt, MFC30.DLL
die weiterverteilbare DLL mit Version 3.0 der MFC-Klassenbibliothek. Ein Upgrade dieser DLL, z. B. in einem hypothetischen MFC 3.1, würde die DLL stattdessen benannt MFC31.DLL
. Wenn Sie den MFC-Quellcode ändern, um eine benutzerdefinierte Version der MFC-DLL zu erstellen, verwenden Sie einen anderen Namen (und vorzugsweise einen ohne "MFC" im Namen).
Siehe auch
Technische Hinweise – nach Nummern geordnet
Technische Hinweise – nach Kategorien geordnet