Vzájemné importy
Export nebo import do jiného spustitelného souboru představuje komplikace, když jsou importy vzájemné (nebo cyklické). Například dvě knihovny DLL importují symboly z sebe navzájem, podobně jako vzájemně rekurzivní funkce.
Problém s vzájemně importujícími spustitelnými soubory (obvykle dll) spočívá v tom, že ani jeden z nich nelze sestavit bez sestavení druhého souboru. Každý proces sestavení vyžaduje jako vstup knihovnu importu vytvořenou jiný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, kolik knihoven DLL se týká nebo jak složité jsou závislosti.
Obecným řešením pro zpracování vzájemných importů je:
Vezměte každou knihovnu DLL zase. (Jakákoli objednávka je proveditelná, i když některé objednávky jsou optimální.) Pokud všechny potřebné knihovny importu existují a jsou aktuální, spusťte LINK a sestavte spustitelný soubor (DLL). Tím se vytvoří knihovna importu. V opačném případě spusťte knihovnu LIB a vytvořte knihovnu importu.
Spuštění knihovny LIB s parametrem /DEF vytvoří další soubor s příponou . Rozšíření EXP. Ten. Soubor EXP se musí použít později k sestavení spustitelného souboru.
Po použití linku nebo knihovny LIB k sestavení všech knihoven importu se vraťte a spusťte LINK a sestavte všechny spustitelné soubory, které nebyly sestaveny v předchozím kroku. Všimněte si, že odpovídající soubor .exp musí být zadán na řádku LINK.
Pokud byste dříve spustili nástroj LIB pro vytvoření knihovny pro import knihovny DLL1, knihovna LIB by vytvořila také soubor DLL1.exp. Při sestavování knihovny DLL1.dlll je nutné použít knihovnu DLL1.exp jako vstup pro propojení.
Následující obrázek znázorňuje řešení pro dva vzájemně importující knihovny DLL, DLL1 a DLL2. Krok 1 je spuštění knihovny LIB se sadou možností /DEF v knihovně DLL1. Krok 1 vytvoří knihovnu DLL1.lib, knihovnu importu a knihovnu DLL1.exp. V kroku 2 se knihovna importu používá k sestavení knihovny DLL2, která pak vytvoří knihovnu importu pro symboly knihovny DLL2. Krok 3 sestaví knihovnu DLL1 pomocí knihovny DLL1.exp a DLL2.lib jako vstup. Všimněte si, že soubor .exp pro knihovnu DLL2 není nutný, protože knihovna LIB nebyla použita k sestavení knihovny DLL2 pro import.
Propojení dvou knihoven DLL se vzájemnými importy
Omezení _AFXEXT
Pro knihovny DLL rozšíření MFC můžete použít symbol preprocesoru _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ém je v tom, že v systému Win32 musíte explicitně deklarovat všechna data, jako __declspec(dllexport)
by se exportovala z knihovny DLL, a __declspec(dllimport)
pokud se má importovat z knihovny DLL. Při definování _AFXEXT
se hlavičky MFC ujistěte, že je správně definován AFX_EXT_CLASS .
Pokud máte více vrstev, jeden symbol, například AFX_EXT_CLASS , nestačí, protože knihovna DLL rozšíření MFC může exportovat nové třídy a 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 versus použití knihovny DLL. Představte si například dvě rozšiřující knihovny MFC DLL, A.dll a B.dll. Každá z nich exportuje některé třídy v 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 ... };
...
Při sestavení A.dll se sestaví /D A_IMPL
a při B.dll se sestaví pomocí /D B_IMPL
. Pomocí samostatných symbolů pro každou knihovnu DLL CExampleB
se exportuje a CExampleA
importuje při vytváření B.dll. CExampleA
se exportuje při vytváření A.dll a importu při použití B.dll (nebo jiného klienta).
Tento typ vrstvení nelze provést při použití předdefinovaných symbolů AFX_EXT_CLASS a _AFXEXT
preprocesoru. Výše popsaná technika tento problém řeší způsobem, který není rozdíl od samotného mechanismu, který mfc používá při sestavování svých aktivních technologií, databází a rozšiřujících knihoven DLL síťové knihovny MFC.
Neexportování celé třídy
Pokud neexportujete celou třídu, musíte zajistit, aby se správně exportovaly potřebné datové položky vytvořené makry MFC. Můžete to udělat tak, že předefinujte AFX_DATA
makro konkrétní třídy. To by se mělo provést pokaždé, 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?
Export funkcí jazyka C++ pro použití ve spustitelných souborech jazyka C
Import do aplikace s použitím deklarace __declspec(dllimport)