Importazioni reciproche

L'esportazione o l'importazione in un altro file eseguibile presenta complicazioni quando le importazioni sono reciproche (o circolari). Ad esempio, due DLL importano simboli l'uno dall'altro, simili alle funzioni ricorsive a vicenda.

Il problema con l'importazione reciproca di file eseguibili (in genere DLL) è che nessuno dei due può essere compilato senza creare l'altro prima. Ogni processo di compilazione richiede, come input, una libreria di importazione prodotta dall'altro processo di compilazione.

La soluzione consiste nell'usare l'utilità LIB con l'opzione /DEF, che produce una libreria di importazione senza compilare il file eseguibile. Usando questa utilità, è possibile compilare tutte le librerie di importazione necessarie, indipendentemente dal numero di DLL coinvolte o dalla complessità delle dipendenze.

La soluzione generale per la gestione delle importazioni reciproche è:

  1. Prendere ogni DLL a sua volta. Qualsiasi ordine è fattibile, anche se alcuni ordini sono più ottimali. Se tutte le librerie di importazione necessarie esistono e sono correnti, eseguire LINK per compilare il file eseguibile (DLL). Ciò produce una libreria di importazione. In caso contrario, eseguire LIB per produrre una libreria di importazione.

    L'esecuzione di LIB con l'opzione /DEF produce un file aggiuntivo con . Estensione EXP. Le. Il file EXP deve essere usato in un secondo momento per compilare il file eseguibile.

  2. Dopo aver usato LINK o LIB per compilare tutte le librerie di importazione, tornare indietro ed eseguire LINK per compilare tutti i file eseguibili non compilati nel passaggio precedente. Si noti che il file con estensione exp corrispondente deve essere specificato nella riga LINK.

    Se l'utilità LIB fosse stata eseguita in precedenza per produrre una libreria di importazione per DLL1, LIB avrebbe prodotto anche il file DLL1.exp. È necessario usare DLL1.exp come input per LINK durante la compilazione di DLL1.dlll.

La figura seguente illustra una soluzione per due DLL che si importano a vicenda, DLL1 e DLL2. Il passaggio 1 consiste nell'eseguire LIB, con l'opzione /DEF impostata in DLL1. Il passaggio 1 genera DLL1.lib, una libreria di importazione e DLL1.exp. Nel passaggio 2, la libreria di importazione viene usata per compilare DLL2, che a sua volta produce una libreria di importazione per i simboli di DLL2. Il passaggio 3 compila DLL1 usando DLL1.exp e DLL2.lib come input. Si noti che un file con estensione exp per DLL2 non è necessario perché LIB non è stato usato per compilare la libreria di importazione di DLL2.

Diagramma che mostra gli input e gli output quando si usano importazioni reciproche per collegare due DLL.
Collegamento di due DLL con importazioni reciproche

Limitazioni di _AFXEXT

È possibile usare il simbolo del _AFXEXT preprocessore per le DLL dell'estensione MFC, purché non si disponga di più livelli di DLL di estensione MFC. Se sono presenti DLL di estensione MFC che chiamano o derivano da classi nelle DLL dell'estensione MFC, che quindi derivano dalle classi MFC, è necessario usare il proprio simbolo del preprocessore per evitare ambiguità.

Il problema è che in Win32 è necessario dichiarare in modo esplicito qualsiasi dato come __declspec(dllexport) se fosse esportato da una DLL e __declspec(dllimport) se deve essere importato da una DLL. Quando si definisce _AFXEXT, le intestazioni MFC assicurano che AFX_EXT_CLASS sia definito correttamente.

Quando si dispone di più livelli, un simbolo come AFX_EXT_CLASS non è sufficiente, perché una DLL di estensione MFC potrebbe esportare nuove classi e importare altre classi da un'altra DLL di estensione MFC. Per risolvere questo problema, usare un simbolo speciale del preprocessore che indica che si sta compilando la DLL stessa rispetto all'uso della DLL. Si supponga, ad esempio, che due DLL di estensione MFC, A.dll e B.dll. Ognuna esporta alcune classi rispettivamente in A.h e B.h. B.dll usa le classi di A.dll. I file di intestazione sono simili al seguente:

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

Quando A.dll viene compilato, viene compilato con /D A_IMPL e quando viene compilato B.dll, viene compilato con /D B_IMPL. Usando simboli separati per ogni DLL, CExampleB viene esportato e CExampleA importato durante la compilazione B.dll. CExampleA viene esportato durante la compilazione di A.dll e importato quando viene usato da B.dll (o da un altro client).

Questo tipo di layering non può essere eseguito quando si usano i simboli predefiniti AFX_EXT_CLASS e _AFXEXT preprocessore. La tecnica descritta in precedenza risolve questo problema in modo diverso dal meccanismo usato da MFC durante la compilazione delle tecnologie attive, del database e delle DLL dell'estensione MFC di rete.

Non esportazione dell'intera classe

Quando non si esporta un'intera classe, è necessario assicurarsi che gli elementi di dati necessari creati dalle macro MFC vengano esportati correttamente. Questa operazione può essere eseguita ridefinendo AFX_DATA la macro della classe specifica. Questa operazione deve essere eseguita ogni volta che non si esporta l'intera classe.

Ad esempio:

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

Cosa vuoi fare?

Scegliere l'argomento su cui visualizzare maggiori informazioni

Vedi anche

Importazione ed esportazione