Sdílet prostřednictvím


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 knihovny importu, 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:

  1. 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é importovací knihovny 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 výrobu importovací 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.

  2. 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 výrobě knihovny importu pro DLL1, měl by LIB vyrobit 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ž postupně 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í knihovny importu DLL2.

Propojení dvou knihoven DLL se vzájemnými importy

Vzájemný import propojený s knihovnami DLL

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

Co chcete udělat?

Co chcete vědět více?

Viz také

Koncepty

Importování a exportování