Importations mutuelles
L’exportation ou l’importation vers un autre fichier exécutable présente des complications lorsque les importations sont mutuelles (ou circulaires). Par exemple, deux DLL importent des symboles les uns des autres, comme pour les fonctions récursives mutuellement.
Le problème lié à l’importation mutuelle des fichiers exécutables (généralement des DLL) est que ni l’un ni l’autre ne peut être généré sans générer l’autre en premier. Chaque processus de génération nécessite, en tant qu’entrée, une bibliothèque d’importation produite par l’autre processus de génération.
La solution consiste à utiliser l’utilitaire LIB avec l’option /DEF, qui produit une bibliothèque d’importation sans générer le fichier exécutable. À l’aide de cet utilitaire, vous pouvez générer toutes les bibliothèques d’importation dont vous avez besoin, quel que soit le nombre de DLL impliquées ou la complexité des dépendances.
La solution générale pour la gestion des importations mutuelles est la suivante :
Prenez chaque DLL à son tour. (Toute commande est réalisable, bien que certaines commandes soient plus optimales.) Si toutes les bibliothèques d’importation nécessaires existent et sont actuelles, exécutez LINK pour générer le fichier exécutable (DLL). Cela produit une bibliothèque d’importation. Sinon, exécutez LIB pour produire une bibliothèque d’importation.
L’exécution de LIB avec l’option /DEF produit un fichier supplémentaire avec un fichier . Extension EXP. Lla. Le fichier EXP doit être utilisé ultérieurement pour générer le fichier exécutable.
Après avoir utilisé LINK ou LIB pour générer toutes les bibliothèques d’importation, revenez en arrière et exécutez LINK pour générer les fichiers exécutables qui n’ont pas été générés à l’étape précédente. Notez que le fichier .exp correspondant doit être spécifié sur la ligne LINK.
Si vous aviez exécuté l’utilitaire LIB précédemment pour produire une bibliothèque d’importation pour DLL1, LIB aurait également produit le fichier DLL1.exp. Vous devez utiliser DLL1.exp comme entrée vers LINK lors de la génération de DLL1.dlll.
L’illustration suivante montre une solution pour deux DLL d’importation mutuelle, DLL1 et DLL2. L’étape 1 consiste à exécuter LIB, avec l’option /DEF définie, sur DLL1. L’étape 1 produit DLL1.lib, une bibliothèque d’importation et DLL1.exp. À l’étape 2, la bibliothèque d’importation est utilisée pour générer DLL2, qui produit à son tour une bibliothèque d’importation pour les symboles de DLL2. L’étape 3 génère DLL1, en utilisant DLL1.exp et DLL2.lib comme entrée. Notez qu’un fichier .exp pour DLL2 n’est pas nécessaire, car LIB n’a pas été utilisé pour générer la bibliothèque d’importation de DLL2.
Liaison de deux DLL à des importations mutuelles
Limitations de _AFXEXT
Vous pouvez utiliser le _AFXEXT
symbole de préprocesseur pour vos DLL d’extension MFC tant que vous n’avez pas plusieurs couches de DLL d’extension MFC. Si vous avez des DLL d’extension MFC qui appellent ou dérivent de classes dans vos propres DLL d’extension MFC, qui dérivent ensuite des classes MFC, vous devez utiliser votre propre symbole de préprocesseur pour éviter toute ambiguïté.
Le problème est que dans Win32, vous devez déclarer explicitement toutes les données comme __declspec(dllexport)
si elles doivent être exportées à partir d’une DLL et __declspec(dllimport)
si elles doivent être importées à partir d’une DLL. Lorsque vous définissez _AFXEXT
, les en-têtes MFC vérifient que AFX_EXT_CLASS est correctement défini.
Lorsque vous avez plusieurs couches, un symbole tel que AFX_EXT_CLASS n’est pas suffisant, car une DLL d’extension MFC peut exporter de nouvelles classes, ainsi que l’importation d’autres classes à partir d’une autre DLL d’extension MFC. Pour résoudre ce problème, utilisez un symbole de préprocesseur spécial qui indique que vous générez la DLL elle-même par rapport à l’utilisation de la DLL. Par exemple, imaginez deux DLL d’extension MFC, A.dll et B.dll. Ils exportent chacune certaines classes dans A.h et B.h, respectivement. B.dll utilise les classes d’A.dll. Les fichiers d’en-tête ressemblent à ceci :
/* 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 ... };
...
Quand A.dll est généré, il est généré avec /D A_IMPL
et quand B.dll est généré, il est généré avec /D B_IMPL
. En utilisant des symboles distincts pour chaque DLL, CExampleB
est exporté et CExampleA
est importé lors de la génération de B.dll. CExampleA
est exporté lors de la génération d’A.dll et importé lorsqu’il est utilisé par B.dll (ou un autre client).
Ce type de superposition ne peut pas être effectué lors de l’utilisation des symboles intégrés AFX_EXT_CLASS et _AFXEXT
de préprocesseur. La technique décrite ci-dessus résout ce problème d’une manière différente du mécanisme utilisé par MFC lors de la génération de ses DLL d’extension Active Technologies, Database et Network MFC.
Ne pas exporter la classe entière
Lorsque vous n’exportez pas une classe entière, vous devez vous assurer que les éléments de données nécessaires créés par les macros MFC sont exportés correctement. Pour ce faire, redéfinissez AFX_DATA
la macro de votre classe spécifique. Cette opération doit être effectuée chaque fois que vous n’exportez pas la classe entière.
Par exemple :
/* 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
Que voulez-vous faire ?
Exporter à partir d’une DLL à l’aide de __declspec(dllexport)
Exporter des fonctions C++ à utiliser dans des exécutables en langage C
Importer dans une application à l'aide de __declspec(dllimport)