Vzájemné importy
Exportování nebo importování jiného spustitelného souboru představuje komplikace, když jsou importy vzájemné (nebo kruhové).Například symboly importování dvou knihoven DLL navzájem, podobně jako vzájemně rekurzivní funkce.
Problém se vzájemným importem spustitelných souborů (obvykle knihoven DLL) je, že nemohou být ani sestaveny bez sestavení toho druhého jako první.Každý proces sestavení vyžaduje jako vstup knihovnu importu, vyrobenou druhým procesem sestavení.
Řešením je použít nástroj LIB s možností /DEF, který vytváří knihovnu importu bez sestavení spustitelného souboru.Pomocí tohoto nástroje můžete sestavit všechny importní knihovny, které potřebujete, bez ohledu na to, o kolik knihoven DLL se jedná nebo jak složité jsou závislosti.
Obecné řešení pro zpracování vzájemných importů je:
Berte postupně každou knihovnu DLL. (Všechna pořadí jsou proveditelná, přestože některá pořadí jsou optimálnější.) Pokud všechny potřebné knihovny importu existují a jsou aktuální, spusťte LINK pro sestavení spustitelného souboru (knihovny DLL).To vytváří knihovnu importu.V opačném případě spusťte LIB pro vytvoření importní knihovny.
Spuštění LIB s parametrem /DEF vytvoří další soubor s příponou .EXP.Soubor .EXP je nutné použít později pro sestavení spustitelného souboru.
Po použití LINK nebo LIB pro sestavení všech knihoven importu přejděte zpět a spusťte LINK pro sestavení všech spustitelných souborů, které nebyly sestaveny v předchozím kroku.Všimněte si, že musí být odpovídající soubor .exp zadán na řádku LINK.
Spustíte-li dříve nástroj LIB k vytvoření importní knihovny pro DLL1, měl by LIB vytvořit také soubor DLL1.exp.Musíte použít DLL1.exp jako vstup do LINK při vytváření DLL1.dll.
Následující ilustrace znázorňuje řešení pro dvě vzájemně importující knihovny DLL1 a DLL2.Krok 1 je spustit LIB s nastavenou možností /DEF na DLL1.Krok 1 vytvoří DLL1.lib, importovací knihovnu a DLL1.exp.V kroku 2 je importovací knihovna použita k vytvoření DLL2, což následně vytvoří knihovnu importu pro symboly DLL2.Krok 3 vytvoří DLL1 pomocí DLL1.exp a DLL2.lib jako vstupu.Všimněte si, že soubor .exp pro DLL2 není nezbytný, protože LIB nebyl použit pro sestavení importní knihovny DLL2.
Propojení dvou knihoven DLL se vzájemnými importy
Omezení _AFXEXT
Můžete použít symbol preprocesoru _AFXEXT pro vaše rozšiřující knihovny DLL, pokud nemáte více vrstev rozšiřujících knihoven DLL.Máte-li rozšiřující knihovny DLL, které volají nebo jsou odvozeny od tříd ve vaší vlastní rozšiřující knihovně DLL, která je potom odvozena od tříd knihovny MFC, je nutné se vyhnout dvojznačnosti použitím vašeho vlastního symbolu preprocesoru.
Problém je, že ve Win32 musíte explicitně deklarovat všechna data jako __declspec(dllexport) pokud mají být exportovány z knihovny DLL a __declspec(dllimport) pokud mají být importovány z knihovny DLL.Když nadefinujete _AFXEXT, hlavičky knihovny MFC se ujistí, že je správně definován AFX_EXT_CLASS.
Pokud máte více vrstev, jeden symbol, jako je AFX_EXT_CLASS, nestačí, protože rozšiřující knihovna DLL může exportovat nové třídy, stejně jako importovat jiné třídy z jiné rozšiřující knihovny DLL.Chcete-li tento problém vyřešit, použijte speciální symbol preprocesoru, který označuje, že vytváříte samotnou knihovnu DLL proti používání dané knihovny DLL.Představte si například dvě rozšiřující knihovny DLL, A.dll a B.dll.Každá exportuje některé třídy v A.h respektive B.h.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ž je A.dll sestavena, je sestavena s /D A_IMPL, a když je B.dll sestavena, je sestavena s /D B_IMPL.Použitím oddělených symbolů pro každou knihovnu DLL je CExampleB exportována a CExampleA importována při vytváření B.dll.CExampleA je exportována při vytváření A.dll a importována při použití B.dll (nebo jiného klienta).
Tento typ vrstvení nelze provést s použitím předdefinovaných symbolů preprocesoru AFX_EXT_CLASS a _AFXEXT.Technika popsaná výše řeší tento problém způsobem ne nepodobným mechanismu, který používá sama knihovna MFC při vytváření jejích knihoven DLL technologií Active, databází a rozšíření sítí.
Neexportování celé třídy
Když neexportujete celou třídu, je nutné zajistit, aby byly nezbytné datové položky, vytvořené makry knihovny MFC, exportovány správně.To lze provést předefinováním AFX_DATA na makro vaší konkrétní třídy.Toto by mělo být provedeno kdykoli, 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