TN033: DLL-Version der MFC

In diesem Hinweis wird beschrieben, wie Sie die MFCxx.DLLMFCxxD.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:

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 DLLScreenCapbeschrieben.

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) und MFCSxxD.LIB (statisch).

  • Release: MFCxx.DLL (kombiniert) und MFCSxx.LIB (statisch).

  • Unicode-Debug: MFCxxUD.DLL (kombiniert) und MFCSxxD.LIB (statisch).

  • Unicode Release: MFCxxU.DLL (kombiniert) und MFCSxxU.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 CWinAppabgeleitete Klasse.

  • Eine MFC-Erweiterungs-DLL muss eine spezielle DllMain. AppWizard stellt eine DllMain 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 exportiert CRuntimeClass . Eine abgeleitete Klasse von CDynLinkLibrary 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 CDialogabgeleitete 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_DATAdeklariert 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.DLLimportiert. 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_SERIALverwenden 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 DLLHUSKsieht 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.DLLListe 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 AfxInitExtensionModulezuordnen.

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 CWinAppeigenes -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.

  1. 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 /Rder 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.DLLkö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.DLLmit 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 eine VERSIONINFO 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.DLLder 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 deleteC++-Datei nicht außer Kraft setzen. Die gleichen Regeln gelten für die restlichen C-Laufzeitspeicherzuweisungsroutinen (z malloc. B. , , realloc, freeund 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