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:
Vytvoření aplikace MFC, která používá verzi knihovny MFC knihovny DLL
Způsob implementace sdílených knihoven dynamického propojení MFC
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 DLLScreenCap
pokroč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é) aMFCSxxD.LIB
(statické).Vydání:
MFCxx.DLL
(kombinované) aMFCSxx.LIB
(statické).Ladění unicode:
MFCxxUD.DLL
(kombinované) aMFCSxxD.LIB
(statické).Verze Unicode:
MFCxxU.DLL
(kombinované) aMFCSxxU.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
CWinApp
třídu.Knihovna DLL rozšíření MFC musí obsahovat speciální
DllMain
. AppWizard poskytujeDllMain
funkci, kterou můžete upravit.Knihovna DLL rozšíření MFC obvykle poskytuje inicializační rutinu
CDynLinkLibrary
pro vytvoření , pokud knihovna DLL rozšíření MFC exportujeCRuntimeClass
typy nebo prostředky do aplikace. Odvozenou tříduCDynLinkLibrary
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 DLLHUSK
pokroč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 CDialog
tří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í _AFXEXT
se 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.DLL
a B.DLL
. Každá z nich exportuje některé třídy do A.H
a B.H
v 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 DLLHUSK
Rozšíř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.DLL
použí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ý CWinApp
objekt. 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.
- 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 aVERSIONINFO
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.DLL
DLL 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
, free
a 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í