Sdílet prostřednictvím


TN033: DLL verze knihovny MFC

Tato poznámka popisuje, jak můžete používat MFCxx.DLL MFCxxD.DLL knihovny dynamického propojení s aplikacemi MFC a rozšiřujícími knihovnami MFC (kde xx je číslo verze MFC). Další informace o běžných knihovnách MFC DLL naleznete v tématu Použití knihovny MFC jako součásti knihovny DLL.

Tato technická poznámka popisuje tři aspekty knihoven DLL. Poslední dvě jsou pro pokročilejší uživatele:

Pokud chcete vytvořit knihovnu DLL pomocí knihovny MFC, kterou lze použít s aplikacemi jiného typu než MFC (označovanou jako běžná knihovna MFC DLL), přečtěte si technickou poznámku 11.

Přehled podpory MFCxx.DLL: Terminologie a soubory

Běžná knihovna MFC DLL: Pomocí běžné knihovny MFC DLL můžete vytvořit samostatnou knihovnu DLL pomocí některých tříd MFC. Rozhraní přes hranice app/DLL jsou rozhraní jazyka C a klientská aplikace nemusí být aplikací MFC.

Běžné knihovny MFC DLL jsou verze knihoven DLL podporovaných v prostředí MFC 1.0. Jsou popsány v technické poznámce 11 a v ukázce DLLScreenCappokročilých konceptů MFC .

Poznámka

Od verze Visual C++ 4.0 je termín USRDLL zastaralý a byl nahrazen běžnou knihovnou MFC DLL, která staticky odkazuje na MFC. Můžete také vytvořit běžnou knihovnu MFC DLL, která dynamicky propojuje knihovnu MFC.

MFC 3.0 (a vyšší) podporuje běžné knihovny MFC DLL se všemi novými funkcemi, včetně OLE a databázových tříd.

AFXDLL: Označuje se také jako sdílená verze knihoven MFC. Jedná se o novou podporu knihovny DLL přidanou v prostředí MFC 2.0. Samotná knihovna MFC se nachází v řadě knihoven DLL (popsaných níže). Klientská aplikace nebo knihovna DLL dynamicky propojuje knihovny DLL, které vyžaduje. Rozhraní přes hranice aplikace/KNIHOVNY DLL jsou rozhraní tříd C++/MFC. Klientská aplikace musí být aplikací MFC. Tato knihovna DLL podporuje všechny funkce MFC 3.0 (výjimka: Kódování UNICODE není podporováno pro databázové třídy).

Poznámka

Od verze Visual C++ 4.0 se tento typ knihovny DLL označuje jako "Rozšiřující knihovna DLL".

Tato poznámka se použije MFCxx.DLL pro odkaz na celou sadu knihovny MFC DLL, která zahrnuje:

  • Ladění: MFCxxD.DLL (kombinované) a MFCSxxD.LIB (statické).

  • Vydání: MFCxx.DLL (kombinované) a MFCSxx.LIB (statické).

  • Ladění unicode: MFCxxUD.DLL (kombinované) a MFCSxxD.LIB (statické).

  • Verze Unicode: MFCxxU.DLL (kombinované) a MFCSxxU.LIB (statické).

Poznámka

Knihovny MFCSxx[U][D].LIB se používají ve spojení se sdílenými knihovnami DLL mfc. Tyto knihovny obsahují kód, který musí být staticky propojený s aplikací nebo knihovnou DLL.

Aplikace odkazuje na odpovídající knihovny importu:

  • Ladění: MFCxxD.LIB

  • Vydání: MFCxx.LIB

  • Ladění unicode: MFCxxUD.LIB

  • Verze Unicode: MFCxxU.LIB

Knihovna DLL rozšíření MFC je knihovna DLL, která rozšiřuje MFCxx.DLL (nebo jiné sdílené knihovny DLL MFC). V této části se spustí architektura komponent MFC. Pokud odvozujete užitečnou třídu z třídy MFC nebo sestavíte jinou sadu nástrojů podobné knihovně MFC, můžete ji umístit do knihovny DLL. Vaše knihovna DLL používá MFCxx.DLL, stejně jako konečná klientská aplikace. Knihovna DLL rozšíření MFC umožňuje opakovaně použitelné třídy typu list, opakovaně použitelné základní třídy a opakovaně použitelné třídy zobrazení a tříd dokumentů.

Výhody a nevýhody

Proč byste měli používat sdílenou verzi knihovny MFC

  • Použití sdílené knihovny může vést k menším aplikacím. (Minimální aplikace, která používá většinu knihovny MFC, je menší než 10 tisíc).

  • Sdílená verze MFC podporuje knihovny DLL rozšíření MFC a běžné knihovny MFC DLL.

  • Je rychlejší vytvořit aplikaci, která používá sdílené knihovny MFC, než staticky propojená aplikace MFC. Je to proto, že není nutné propojit samotnou knihovnu MFC. Platí to zejména v DEBUG buildech, kde linker musí komprimovat informace o ladění. Když se vaše aplikace propojila s knihovnou DLL, která již obsahuje informace o ladění, je méně informací o ladění, které je potřeba komprimovat.

Proč byste neměli používat sdílenou verzi prostředí MFC:

  • Odeslání aplikace, která používá sdílenou knihovnu, vyžaduje odeslání MFCxx.DLL a další knihovny s programem. MFCxx.DLL je volně redistribuovatelný jako mnoho knihoven DLL, ale stále musíte nainstalovat knihovnu DLL v instalačním programu. Budete také muset odeslat další redistribuovatelné knihovny používané programem i knihovnami MFC DLL.

Jak napsat knihovnu DLL rozšíření MFC

Knihovna DLL rozšíření MFC je knihovna DLL, která obsahuje třídy a funkce pro rozšíření funkcí tříd MFC. Knihovna DLL rozšíření MFC používá sdílené knihovny MFC DLL stejným způsobem, jakým je aplikace používá, s několika dalšími aspekty:

  • Proces sestavení je podobný sestavení aplikace, která používá sdílené knihovny MFC s několika dalšími možnostmi kompilátoru a linkeru.

  • Knihovna DLL rozšíření MFC nemá odvozenou CWinApptřídu.

  • Knihovna DLL rozšíření MFC musí obsahovat speciální DllMain. AppWizard poskytuje DllMain funkci, kterou můžete upravit.

  • Knihovna DLL rozšíření MFC obvykle poskytuje inicializační rutinu CDynLinkLibrarypro vytvoření , pokud knihovna DLL rozšíření MFC exportuje CRuntimeClass typy nebo prostředky do aplikace. Odvozenou třídu CDynLinkLibrary lze použít v případě, že data pro jednotlivé aplikace musí být udržována knihovnou DLL rozšíření MFC.

Tyto aspekty jsou podrobněji popsány níže. Projděte si také ukázku DLLHUSKpokročilých konceptů MFC . Ukazuje, jak:

  • Sestavte aplikaci pomocí sdílených knihoven. (DLLHUSK.EXE je aplikace MFC, která dynamicky propojuje knihovny MFC a další knihovny DLL.)

  • Vytvořte knihovnu DLL rozšíření MFC. (Ukazuje, jak se při vytváření knihovny DLL rozšíření MFC používají speciální příznaky, jako _AFXEXT je použití.)

  • Sestavte dva příklady rozšiřujících knihoven DLL mfc. Jeden ukazuje základní strukturu knihovny MFC extension DLL s omezenými exporty (TESTDLL1) a druhý ukazuje export celého rozhraní třídy (TESTDLL2).

Klientská aplikace i všechny knihovny DLL rozšíření MFC musí používat stejnou verzi MFCxx.DLL. Postupujte podle konvencí knihoven MFC DLL a poskytněte ladicí i vydané verze/release knihovny DLL rozšíření MFC. Tento postup umožňuje klientským programům vytvářet ladicí i vydané verze svých aplikací a propojit je s odpovídající verzí ladění nebo vydané verze všech knihoven DLL.

Poznámka

Vzhledem k tomu, že problémy s úpravou a exportem názvů jazyka C++, může se seznam exportu z knihovny DLL rozšíření MFC lišit mezi verzí ladění a verzí stejné knihovny DLL a knihoven DLL pro různé platformy. Verze MFCxx.DLL má přibližně 2000 exportovaných vstupních bodů. MFCxxD.DLL Ladění má přibližně 3000 exportovaných vstupních bodů.

Rychlá poznámka ke správě paměti

Část s názvem Správa paměti na konci této technické poznámky popisuje implementaci MFCxx.DLL se sdílenou verzí mfc. Informace, které potřebujete vědět k implementaci pouze knihovny DLL rozšíření MFC, jsou popsány zde.

MFCxx.DLL a všechny knihovny DLL rozšíření MFC načtené do adresního prostoru klientské aplikace budou používat stejný alokátor paměti, načítání prostředků a další globální stavy MFC, jako by byly ve stejné aplikaci. Je to důležité, protože knihovny knihovny DLL jiné než MFC a běžné knihovny MFC DLL, které staticky odkazují na MFC, dělají přesně opačně: každá knihovna DLL přiděluje ze svého vlastního fondu paměti.

Pokud knihovna DLL rozšíření MFC přiděluje paměť, může tato paměť volně intermixovat s jakýmkoli jiným objektem přiděleným aplikací. Pokud dojde také k chybě aplikace, která používá sdílené knihovny MFC, operační systém udržuje integritu jakékoli jiné aplikace MFC, která sdílí knihovnu DLL.

Podobně se ostatní "globální" mfc stavy, jako je aktuální spustitelný soubor pro načtení prostředků, také sdílejí mezi klientskou aplikací, všemi rozšiřujícími knihovnami MFC DLL a MFCxx.DLL samotným.

Sestavení knihovny DLL rozšíření MFC

Pomocí AppWizard můžete vytvořit projekt knihovny DLL rozšíření MFC a automaticky vygeneruje odpovídající nastavení kompilátoru a linkeru. Vygeneruje DllMain také funkci, kterou můžete upravit.

Pokud převádíte existující projekt na knihovnu DLL rozšíření MFC, začněte standardním nastavením, které se sestaví pomocí sdílené verze mfc. Pak proveďte následující změny:

  • Přidejte /D_AFXEXT do příznaků kompilátoru. V dialogovém okně Vlastnosti projektu vyberte kategorii preprocesoru C/C++>. Přidejte _AFXEXT do pole Definovat makra a oddělte jednotlivé položky středníky.

  • Odeberte přepínač kompilátoru /Gy . V dialogovém okně Vlastnosti projektu vyberte kategorii generování kódu C/C++>. Ujistěte se, že není povolena vlastnost Povolit propojení na úrovni funkce. Toto nastavení usnadňuje export tříd, protože linker neodkazuje neodkazované funkce. Pokud původní projekt vytvořil běžnou knihovnu MFC DLL, která je staticky propojená s mfc, změňte možnost kompilátoru /MT (nebo /MTd) na /MD (nebo /MDd).

  • Sestavte knihovnu exportu /DLL s možností PROPOJIT. Tato možnost se nastaví při vytvoření nového cíle a jako typ cíle zadáte dynamickou knihovnu Win32.

Změna souborů hlaviček

Obvyklým cílem knihovny DLL rozšíření MFC je exportovat některé běžné funkce do jedné nebo více aplikací, které mohou tuto funkci používat. Knihovna DLL v podstatě exportuje třídy a globální funkce pro použití klientskými aplikacemi.

Chcete-li zajistit, aby každá členská funkce byla označena pro import nebo export podle potřeby, použijte zvláštní deklarace __declspec(dllexport) a __declspec(dllimport). Když klientské aplikace používají vaše třídy, chcete, aby byly deklarovány jako __declspec(dllimport). Při samotné sestavení knihovny DLL rozšíření MFC by měly být funkce deklarovány jako __declspec(dllexport). Sestavená knihovna DLL musí také exportovat funkce, aby je klientské programy mohly svázat při načítání.

Pokud chcete exportovat celou třídu, použijte AFX_EXT_CLASS ji v definici třídy. Architektura toto makro definuje jako __declspec(dllexport) kdy _AFXDLL a _AFXEXT je definováno, ale definuje ho, jako __declspec(dllimport) když _AFXEXT není definováno. _AFXEXT je definován pouze při sestavování knihovny DLL rozšíření MFC. Příklad:

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

Neexportování celé třídy

Někdy můžete chtít exportovat jenom jednotlivé nezbytné členy předmětu. Pokud například exportujete -odvozenou CDialogtřídu, možná budete muset exportovat pouze konstruktor a DoModal volání. Tyto členy můžete exportovat pomocí souboru DEF knihovny DLL, ale můžete je použít AFX_EXT_CLASS stejně jako u jednotlivých členů, které potřebujete exportovat.

Příklad:

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

Když to uděláte, můžete narazit na další problém, protože neexportujete všechny členy třídy. Problém spočívá v tom, že makra MFC fungují. Několik pomocných maker mfc ve skutečnosti deklaruje nebo definuje datové členy. Knihovna DLL musí exportovat i tyto datové členy.

Makro DECLARE_DYNAMIC je například definováno následujícím způsobem při vytváření rozšiřující knihovny DLL knihovny MFC:

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

Čára, která začíná static AFX_DATA deklarovat statický objekt uvnitř vaší třídy. Chcete-li exportovat tuto třídu správně a získat přístup k informacím modulu runtime z klientského exe, je nutné exportovat tento statický objekt. Vzhledem k tomu, že statický objekt je deklarován modifikátorem AFX_DATA, stačí definovat AFX_DATA pouze při __declspec(dllexport) sestavování knihovny DLL. Definujte ho jako __declspec(dllimport) při sestavování spustitelného souboru klienta.

Jak je popsáno výše, AFX_EXT_CLASS je již definován tímto způsobem. Stačí předefinovat AFX_DATA , aby byla stejná jako AFX_EXT_CLASS u definice třídy.

Příklad:

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

MFC vždy používá AFX_DATA symbol u datových položek, které definuje v rámci maker, takže tato technika bude fungovat pro všechny takové scénáře. Bude například fungovat pro DECLARE_MESSAGE_MAP.

Poznámka

Pokud exportujete celou třídu místo vybraných členů třídy, statické datové členy se automaticky exportují.

Stejnou techniku můžete použít k automatickému exportu operátoru CArchive extrakce pro třídy, které používají DECLARE_SERIAL a IMPLEMENT_SERIAL makra. Exportujte operátor archivu závorkou deklarací tříd (umístěných v souboru hlavičky) s následujícím kódem:

#undef AFX_API
#define AFX_API AFX_EXT_CLASS

/* your class declarations here */

#undef AFX_API
#define AFX_API

Omezení _AFXEXT

Pro knihovny DLL rozšíření MFC můžete použít symbol předprocesoru _AFXEXT , pokud nemáte více vrstev rozšiřujících knihoven DLL MFC. Pokud máte knihovny DLL rozšíření MFC, které volají nebo odvozují z tříd ve vlastních rozšiřujících knihovnách MFC DLL, které pak odvozují z tříd MFC, je nutné použít vlastní symbol preprocesoru, abyste se vyhnuli nejednoznačnosti.

Problémem je, že v systému Win32 musíte explicitně deklarovat všechna data pro __declspec(dllexport) export z knihovny DLL a __declspec(dllimport) importovat je z knihovny DLL. Při definování _AFXEXTse hlavičky MFC ujistěte, že AFX_EXT_CLASS jsou správně definovány.

Pokud máte více vrstev, jeden symbol, například AFX_EXT_CLASS není dostatečný: Knihovna DLL rozšíření MFC může exportovat vlastní třídy a také importovat další třídy z jiné knihovny DLL rozšíření MFC. Chcete-li tento problém vyřešit, použijte speciální preprocesor symbol, který indikuje, že vytváříte samotnou knihovnu DLL namísto použití knihovny DLL. Představte si například dvě rozšiřující knihovny DLL MFC, A.DLLa B.DLL. Každá z nich exportuje některé třídy do A.H a B.Hv uvedeném pořadí. B.DLL používá třídy z A.DLL. Soubory hlaviček by vypadaly přibližně takto:

/* 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 ... */ };

Když A.DLL je sestavený, je sestaven pomocí /DA_IMPL a kdy B.DLL je sestaven, je sestavený pomocí /DB_IMPL. Pomocí samostatných symbolů pro každou knihovnu DLL CExampleB je exportován a CExampleA importován při sestavování B.DLL. CExampleA je exportován při sestavování A.DLL a importu při použití jiným klientem B.DLL .

Tento typ vrstvení nelze provést při použití předdefinovaných AFX_EXT_CLASS a _AFXEXT preprocesorových symbolů. Výše popsaná technika tento problém řeší stejným způsobem jako mfc. MFC používá tuto techniku při vytváření rozšiřujících knihoven DLL OLE, Database a Network MFC.

Stále neexportuje celou třídu.

Opět budete muset věnovat zvláštní pozornost, když neexportujete celou třídu. Ujistěte se, že jsou správně exportovány potřebné datové položky vytvořené makry MFC. Můžete to udělat tak, že předefinujete AFX_DATA makro konkrétní třídy. Předefinujte ji vždy, když neexportujete celou třídu.

Příklad:

// 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

Tady je kód, který byste měli umístit do hlavního zdrojového souboru pro knihovnu DLL s příponou MFC. Měla by pocházet po standardu. Když použijete AppWizard k vytvoření počátečních souborů pro knihovnu DLL rozšíření MFC, poskytuje DllMain pro vás.

#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
}

Volání zachytává AfxInitExtensionModule třídy modulu runtime (CRuntimeClass struktury) a jeho objektové továrny (COleObjectFactory objekty) pro pozdější použití při vytváření objektu CDynLinkLibrary . (volitelné) volání AfxTermExtensionModule umožňuje mfc vyčistit knihovnu DLL rozšíření MFC, když se každý proces odpojí (což se stane při ukončení procesu nebo při uvolnění knihovny DLL voláním FreeLibrary ) z knihovny DLL rozšíření MFC. Vzhledem k tomu, že většina rozšiřujících knihoven DLL MFC není dynamicky načtena (obvykle jsou propojené prostřednictvím knihoven importu), AfxTermExtensionModule volání obvykle není nutné.

Pokud se vaše aplikace dynamicky načte a uvolní knihovny DLL rozšíření MFC, nezapomeňte volat AfxTermExtensionModule , jak je znázorněno výše. Nezapomeňte také použít AfxLoadLibrary a AfxFreeLibrary (místo funkcí LoadLibrary Win32 a FreeLibrary) pokud vaše aplikace používá více vláken nebo pokud dynamicky načte rozšiřující knihovnu DLL knihovny MFC. Použití AfxLoadLibrary a AfxFreeLibrary zajištění, že spouštěcí a vypínání kód, který se spustí při načtení a uvolnění knihovny DLL rozšíření MFC, není poškozen globální stav MFC.

Soubor AFXDLLX.H hlaviček obsahuje speciální definice pro struktury používané v rozšiřujících knihovnách DLL MFC, jako je definice pro AFX_EXTENSION_MODULE a CDynLinkLibrary.

Globální rozšířeníDLL musí být deklarováno, jak je znázorněno. Na rozdíl od 16bitové verze knihovny MFC můžete během této doby přidělit paměť a volat funkce MFC, protože MFCxx.DLL je plně inicializována časem volání.DllMain

Sdílení prostředků a tříd

Jednoduché knihovny DLL rozšíření MFC potřebují exportovat pouze několik funkcí s nízkou šířkou pásma do klientské aplikace a nic dalšího. Knihovny DLL náročné na uživatelské rozhraní mohou chtít exportovat prostředky a třídy jazyka C++ do klientské aplikace.

Export prostředků se provádí prostřednictvím seznamu prostředků. V každé aplikaci je ingly propojený seznam CDynLinkLibrary objektů. Při hledání prostředku se většina standardních implementací MFC, které načítají prostředky, nejprve prohlédněte aktuální modul prostředků (AfxGetResourceHandle) a pokud nebyl nalezen, projděte si seznam CDynLinkLibrary objektů, které se pokoušejí načíst požadovaný prostředek.

Dynamické vytváření objektů C++ s názvem třídy C++ je podobné. Mechanismus deserializace objektu MFC musí mít zaregistrované všechny CRuntimeClass objekty, aby bylo možné rekonstruovat dynamickým vytvořením objektu jazyka C++ požadovaného typu na základě toho, co bylo uloženo dříve.

Pokud chcete, aby klientská aplikace používala třídy v knihovně DLL rozšíření MFC, které jsou DECLARE_SERIAL, budete muset exportovat třídy, aby byly viditelné pro klientskou aplikaci. Dělá se to taky procházením CDynLinkLibrary seznamu.

V ukázce DLLHUSKRozšířených konceptů MFC vypadá seznam přibližně takto:

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

Položka MFCxx.DLL je obvykle poslední v seznamu prostředků a tříd. MFCxx.DLL obsahuje všechny standardní prostředky MFC, včetně řetězců výzvy pro všechny standardní ID příkazů. Umístění na konec seznamu umožňuje, aby knihovny DLL i samotná klientská aplikace spoléhaly na sdílené prostředky v objektu MFCxx.DLL, a ne na jejich vlastní kopie.

Sloučení prostředků a názvů tříd všech knihoven DLL do názvového prostoru klientské aplikace má nevýhodu, že musíte být opatrní, jaké ID nebo názvy vyberete. Tuto funkci můžete zakázat tak, že neexportujete prostředky nebo CDynLinkLibrary objekt do klientské aplikace. Ukázka DLLHUSK spravuje prostor pro název sdíleného prostředku pomocí více souborů hlaviček. Další tipy k používání sdílených souborů prostředků najdete v technické poznámce 35 .

Inicializace knihovny DLL

Jak je uvedeno výše, obvykle budete chtít vytvořit CDynLinkLibrary objekt pro export prostředků a tříd do klientské aplikace. Abyste mohli knihovnu DLL inicializovat, musíte zadat exportovaný vstupní bod. Minimálně je to rutina void , která nepřijímá žádné argumenty a nevrací nic, ale může to být cokoli, co se vám líbí.

Každá klientská aplikace, která chce použít vaši knihovnu DLL, musí tuto inicializační rutinu volat, pokud použijete tento přístup. Tento objekt můžete přidělit CDynLinkLibrary také ve svém DllMain těsně po volání AfxInitExtensionModule.

Inicializační rutina musí vytvořit CDynLinkLibrary objekt v haldě aktuální aplikace připojené k informacím knihovny DLL rozšíření MFC. Můžete to udělat tak, že definujete funkci, jako je tato:

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

Název rutiny InitXxxDLL v tomto příkladu může být libovolný. Nemusí to být extern "C", ale usnadňuje údržbu seznamu exportů.

Poznámka

Pokud používáte knihovnu DLL rozšíření MFC z běžné knihovny MFC DLL, musíte tuto inicializační funkci exportovat. Tato funkce musí být volána z běžné knihovny MFC DLL před použitím všech rozšiřujících tříd knihovny DLL knihovny MFC nebo prostředků.

Export položek

Jednoduchým způsobem, jak exportovat třídy, je použít __declspec(dllimport) a __declspec(dllexport) pro každou třídu a globální funkci, kterou chcete exportovat. Je to mnohem jednodušší, ale je méně efektivní než pojmenování jednotlivých vstupních bodů v souboru DEF, jak je popsáno níže. Je to proto, že máte menší kontrolu nad tím, jaké funkce se exportují. A funkce nemůžete exportovat podle řad. TESTDLL1 a TESTDLL2 použít tuto metodu k exportu svých položek.

Efektivnější metodou je exportovat každou položku tak, že ji pojmete v souboru DEF. Tuto metodu MFCxx.DLLpoužívá . Vzhledem k tomu, že exportujeme selektivně z naší knihovny DLL, musíme se rozhodnout, která konkrétní rozhraní chceme exportovat. Je obtížné, protože je nutné zadat mangled názvy linkeru ve formě položek v souboru DEF. Nevyexportujte žádnou třídu C++, pokud pro ni opravdu nepotřebujete symbolický odkaz.

Pokud jste dříve vyzkoušeli export tříd C++ se souborem DEF, možná budete chtít vytvořit nástroj pro automatické vygenerování tohoto seznamu. Můžete ho provést pomocí dvoufázového procesu propojení. Propojte knihovnu DLL jednou bez exportu a povolte linkeru vygenerovat soubor MAP. Soubor MAP obsahuje seznam funkcí, které by se měly exportovat. S určitým uspořádáním ho můžete použít k vygenerování položek exportu pro soubor DEF. Seznam exportů pro MFCxx.DLL rozšiřující knihovny DLL OLE a Database MFC, několik tisíc v číslech, byl vygenerován s takovým procesem (i když není plně automatický a vyžaduje několik ručních ladění jednou za chvíli).

CWinApp vs. CDynLinkLibrary

Knihovna DLL rozšíření MFC nemá vlastní odvozený CWinAppobjekt. Místo toho musí pracovat s CWinApp-odvozeným objektem klientské aplikace. Znamená to, že klientská aplikace vlastní hlavní čerpadlo zpráv, nečinnou smyčku atd.

Pokud vaše knihovna DLL rozšíření MFC potřebuje udržovat další data pro každou aplikaci, můžete z CDynLinkLibrary ní odvodit novou třídu a vytvořit ji v rutině InitXxxDLL popsané výše. Při spuštění může knihovna DLL zkontrolovat seznam CDynLinkLibrary objektů aktuální aplikace a najít objekt pro konkrétní knihovnu DLL rozšíření MFC.

Použití prostředků v implementaci knihovny DLL

Jak je uvedeno výše, výchozí načtení prostředků provede seznam CDynLinkLibrary objektů, které hledají první exe nebo DLL, které mají požadovaný prostředek. Všechna rozhraní MFC API a veškerý interní kód slouží AfxFindResourceHandle k procházení seznamu prostředků k vyhledání libovolného prostředku bez ohledu na to, kde se nachází.

Pokud chcete načíst pouze prostředky z konkrétního místa, použijte rozhraní API AfxGetResourceHandle a AfxSetResourceHandle uložte starý popisovač a nastavte nový popisovač. Než se vrátíte do klientské aplikace, nezapomeňte starý popisovač prostředku obnovit. Ukázková TESTDLL2 používá tento přístup k explicitnímu načtení nabídky.

Procházení seznamu má určité nevýhody: je trochu pomalejší a vyžaduje správu rozsahů ID prostředků. Má výhodu, že klientská aplikace, která odkazuje na několik rozšiřujících knihoven DLL, může použít libovolný prostředek poskytovaný knihovnou DLL, aniž by bylo nutné zadat popisovač instance knihovny DLL. AfxFindResourceHandle je rozhraní API sloužící k procházení seznamu prostředků k vyhledání dané shody. Vezme název a typ prostředku a vrátí popisovač prostředku, kde ho nejprve najde, nebo hodnotu NULL.

Zápis aplikace, která používá verzi knihovny DLL

Požadavky aplikace

Aplikace, která používá sdílenou verzi knihovny MFC, musí dodržovat několik základních pravidel:

  • Musí mít CWinApp objekt a dodržovat standardní pravidla pro čerpadlo zpráv.

  • Musí se zkompilovat se sadou požadovaných příznaků kompilátoru (viz níže).

  • Musí propojit knihovny importu MFCxx. Nastavením požadovaných příznaků kompilátoru určují hlavičky MFC v době propojení, se kterou knihovnou by se měla aplikace propojit.

  • Chcete-li spustitelný soubor spustitelný soubor, MFCxx.DLL musí být na cestě nebo v systémovém adresáři Systému Windows.

Sestavování s využitím vývojového prostředí

Pokud používáte interní soubor pravidel s většinou standardních výchozích hodnot, můžete projekt snadno změnit tak, aby se sestavil verze knihovny DLL.

Následující krok předpokládá, že máte správně funkční aplikaci MFC propojenou s NAFXCWD.LIB (pro ladění) a NAFXCW.LIB (pro vydání) a chcete ji převést tak, aby používala sdílenou verzi knihovny MFC. Používáte prostředí sady Visual Studio a máte interní soubor projektu.

  1. V nabídce Projekty vyberte Vlastnosti. Na stránce Obecné v části Výchozí hodnoty projektu nastavte třídy služby Microsoft Foundation tak, aby používaly mfc ve sdílené knihovně DLL (MFCxx(d).dll).

Sestavení pomocí nástroje NMAKE

Pokud používáte funkci externího souboru pravidel kompilátoru nebo používáte NMAKE přímo, budete muset upravit soubor pravidel tak, aby podporoval požadované možnosti kompilátoru a linkeru.

Požadované příznaky kompilátoru:

  • /D_AFXDLL /MD /D_AFXDLL

Standardní hlavičky MFC potřebují, _AFXDLL aby bylo možné definovat symbol.

  • /MD Aplikace musí používat knihovnu DLL knihovny runtime jazyka C.

Všechny ostatní příznaky kompilátoru se řídí výchozími nastaveními MFC (například _DEBUG pro ladění).

Upravte seznam knihoven linkeru. Změňte NAFXCWD.LIB na MFCxxD.LIB a změňte NAFXCW.LIB na MFCxx.LIB. Nahraďte LIBC.LIB MSVCRT.LIB. Stejně jako u jakékoli jiné knihovny MFC je důležité, aby MFCxxD.LIB byla umístěna před všemi knihovnami C-runtime.

Volitelně můžete přidat /D_AFXDLL možnosti kompilátoru prostředků vydané verze i ladění (ten, který prostředky ve skutečnosti kompiluje)./R Tato možnost zmenší konečný spustitelný soubor sdílením prostředků, které jsou přítomné v knihovnách MFC DLL.

Po provedení těchto změn se vyžaduje úplné opětovné sestavení.

Sestavení ukázek

Většina ukázkových programů MFC se dá sestavit z Visual C++ nebo ze sdíleného souboru MAKEFILE kompatibilního s NMAKE z příkazového řádku.

Pokud chcete převést některou z těchto ukázek na použití MFCxx.DLL, můžete soubor MAK načíst do visual C++ a nastavit možnosti projektu, jak je popsáno výše. Pokud používáte sestavení NMAKE, můžete zadat AFXDLL=1 na příkazovém řádku NMAKE a tím sestavit ukázku pomocí sdílených knihoven MFC.

Ukázka knihovny DLLHUSK upřesňujících konceptů MFC je sestavena s verzí knihovny MFC. Tato ukázka ukazuje nejen, jak vytvořit aplikaci propojenou s MFCxx.DLL, ale také ilustruje další funkce možnosti balení knihovny MFC DLL, jako jsou knihovny DLL rozšíření MFC popsané dále v této technické poznámce.

Poznámky k balení

Verze knihoven DLL (MFCxx.DLL a MFCxxU.DLL) jsou volně redistribuovatelné. Ladicí verze knihoven DLL nejsou volně redistribuovatelné a měly by se používat pouze při vývoji aplikace.

Ladicí knihovny DLL jsou k dispozici s informacemi o ladění. Pomocí ladicího programu jazyka Visual C++ můžete trasovat spuštění aplikace i knihovny DLL. Knihovny DLL vydané verze (MFCxx.DLL a MFCxxU.DLL) neobsahují informace o ladění.

Pokud knihovny DLL přizpůsobíte nebo znovu sestavíte, měli byste je volat jinak než MFCxx. Soubor MFCDLL.MAK MFC SRC popisuje možnosti sestavení a obsahuje logiku pro přejmenování knihovny DLL. Přejmenování souborů je nezbytné, protože tyto knihovny DLL jsou potenciálně sdíleny mnoha aplikacemi MFC. Vlastní verze knihoven MFC DLL nahradí ty, které jsou nainstalovány v systému, může narušit jinou aplikaci MFC pomocí sdílených knihoven MFC DLL.

Opětovné sestavení knihoven MFC DLL se nedoporučuje.

Způsob implementace knihovny MFCxx.DLL

Následující část popisuje, jak se implementuje knihovna MFC DLL (MFCxx.DLL a MFCxxD.DLL). Pochopení zde zadaných podrobností také není důležité, pokud vše, co chcete udělat, je použít knihovnu MFC DLL s vaší aplikací. Podrobnosti zde nejsou nezbytné pro pochopení, jak zapsat knihovnu DLL rozšíření MFC, ale pochopení této implementace vám může pomoct se zápisem vlastní knihovny DLL.

Přehled implementace

Knihovna MFC DLL je opravdu zvláštní případ knihovny DLL rozšíření MFC, jak je popsáno výše. Má velký počet exportů pro velký počet tříd. Existuje několik dalších věcí, které v knihovně MFC DLL děláme, aby byla ještě více speciální než běžná rozšiřující knihovna DLL knihovny MFC.

Win32 většinu práce

16bitová verze mfc potřebovala řadu speciálních technik, včetně dat pro jednotlivé aplikace v segmentu zásobníku, speciálních segmentů vytvořených některými kódy sestavení 80x86, kontexty výjimek pro jednotlivé procesy a další techniky. Win32 přímo podporuje data pro zpracování v knihovně DLL, což je to, co chcete většinu času. Ve většině případů MFCxx.DLL je pouze NAFXCW.LIB zabalena v knihovně DLL. Pokud se podíváte na zdrojový kód MFC, najdete několik #ifdef _AFXDLL případů, protože není k dispozici mnoho speciálních případů, které je potřeba provést. Speciální případy, které existují konkrétně pro řešení Win32 ve Windows 3.1 (jinak označované jako Win32s). Win32s nepodporuje přímo data knihovny DLL pro zpracování. Knihovna MFC DLL musí k získání místních dat použít rozhraní API Win32 (Thread-Local Storage) Win32.

Dopad na zdroje knihoven, další soubory

_AFXDLL Dopad verze na běžné zdroje a hlavičky knihovny tříd MFC je relativně menší. Existuje speciální soubor verze (AFXV_DLL.H) a další hlavičkový soubor (AFXDLL_.H) zahrnutý v hlavní AFXWIN.H hlavičce. Hlavička AFXDLL_.H obsahuje CDynLinkLibrary třídu a další podrobnosti implementace _AFXDLL aplikací i rozšiřujících knihoven DLL mfc. Hlavička AFXDLLX.H je k dispozici pro vytváření rozšiřujících knihoven DLL mfc (podrobnosti najdete výše).

Běžné zdroje knihovny MFC v prostředí MFC SRC mají v #ifdef další podmíněný kód _AFXDLL . Další zdrojový soubor (DLLINIT.CPP) obsahuje dodatečný kód inicializace knihovny DLL a další připevnění pro sdílenou verzi mfc.

Pro sestavení sdílené verze mfc jsou k dispozici další soubory. (Podrobnosti o sestavení knihovny DLL najdete níže.)

  • Dva soubory DEF slouží k exportu vstupních bodů knihovny MFC DLL pro ladicí (MFCxxD.DEF) a vydané (MFCxx.DEF) verze knihovny DLL.

  • Soubor RC (MFCDLL.RC) obsahuje všechny standardní prostředky MFC a VERSIONINFO prostředek knihovny DLL.

  • K dispozici je soubor CLW (MFCDLL.CLW), který umožňuje procházení tříd MFC pomocí TřídyWizard. Tato funkce není konkrétní pro verzi knihovny MFC knihovny DLL.

Správa paměti

Aplikace, která používá MFCxx.DLL společný alokátor paměti poskytovaný knihovnou MSVCRTxx.DLLDLL sdíleného modulu C-runtime. Aplikace, všechny knihovny DLL rozšíření MFC a knihovny MFC DLL používají tento alokátor sdílené paměti. Pomocí sdílené knihovny DLL pro přidělení paměti mohou knihovny MFC DLL přidělit paměť, která je později uvolněna aplikací nebo naopak. Vzhledem k tomu, že aplikace i knihovna DLL musí používat stejný alokátor, neměli byste přepsat globální operator new jazyk C++ nebo operator delete. Stejná pravidla platí pro zbývající rutiny přidělování paměti za běhu jazyka C (například malloc, realloc, freea další).

Ordinaly a třídy __declspec(dllexport) a pojmenování knihoven DLL

Nepoužíváme class__declspec(dllexport) funkce kompilátoru jazyka C++. Místo toho je seznam exportů součástí zdrojů knihovny tříd (MFCxx.DEF a MFCxxD.DEF). Exportuje se pouze výběrová sada vstupních bodů (funkcí a dat). Jiné symboly, jako jsou například privátní implementační funkce nebo třídy MFC, se neexportují. Všechny exporty se provádějí podle řadových názvů bez názvu řetězce v tabulce s názvem rezidenta nebo nerezidenta.

Použití class__declspec(dllexport) může být proveditelnou alternativou pro vytváření menších knihoven DLL, ale ve velkých knihovnách DLL, jako je MFC, má výchozí mechanismus exportu omezení efektivity a kapacity.

To vše znamená, že můžeme zabalit velké množství funkcí ve vydané verzi MFCxx.DLL , která je pouze kolem 800 kB, aniž bychom museli ohrozit velkou rychlost provádění nebo načítání. MFCxx.DLL byla by větší 100 kB, pokud tato technika nebyla použita. Technika umožňuje přidat další vstupní body na konec souboru DEF. Umožňuje jednoduchou správu verzí bez ohrožení rychlosti a efektivity exportu podle řad. Revize hlavní verze v knihovně tříd MFC změní název knihovny. To znamená, MFC30.DLL že distribuovatelné knihovny DLL obsahující verzi 3.0 knihovny tříd MFC. Například upgrade této knihovny DLL v hypotetické knihovně MFC 3.1 by knihovna DLL pojmenovala MFC31.DLL místo toho. Znovu platí, že pokud upravíte zdrojový kód MFC tak, aby vytvořil vlastní verzi knihovny MFC DLL, použijte jiný název (a nejlépe jeden bez knihovny MFC v názvu).

Viz také

Technické poznámky podle čísel
Technické poznámky podle kategorií