1. Problèmes de dépendance entre bibliothèques dynamiques et plugins
- Symptôme typique : Le plugin affiche une erreur "non défini" lors de l'appel aux interfaces du programme principal / de la bibliothèque dynamique.
- Cause fondamentale : Dépendances manquantes lors de la compilation / de l'édition des liens (déclarations d'en-tête non définies, symboles non exportés, mauvaise configuration des chemins de bibliothèque).
- Solution clé : Extraire la logique commune (comme
DataExchangeGlobal) en une bibliothèque dynamique indépendante, permettant au programme principle et aux plugins de la réutiliser par "lien de bibliothèque" plutôt que par dépendance directe à un exécutable.
2. Problèmes de dépendances circulaires
- Symptôme typique : La bibliothèque dynamique a besoin d'appeler les interfaces du programme principal, tandis que le programme principal dépend de la bibliothèque dynamique, provoquant des anomalies lors de la compilation / de l'exécution.
- Cause fondamentale : Dépendance bidirectionnelle entre modules (bibliothèque dynamique ←→ programme principal), violant la norme d'ingénierie "dépendance unidirectionnelle".
- Solution clé : Utiliser une couche d'abstraction d'interface (comme `ICommInterface**) comme intermédiaire, permettant à la bibliothèque dynamique de "dépendre de l'interface plutôt que de l'implémentation". Le programme principal implémente l'interface et l'enregistre auprès de la bibliothèque dynamique, coupant complètement la dépendance bidirectionnelle.
3. Problèmes de dépendance liés aux structures de données partagées
- Symptôme typique : La bibliothèque dynamique et le programme principal ont besoin de structures de données comme
PluginConfigData, mais la référence directe aux fichiers d'en-tête du programme principal entraîne un couplage de dépendance ; ou le plugin accède aux variables de classe du programme principal. - Cause fondamentale : Structure de données fortement liée à la logique métier (comme les interfaces de plugin), sans "extraction de données pures".
- Solution clé : Placer les structures de données partagées (comme
PluginConfigData) dans un fichier d'en-tête "sans dépendance métier" (commeCommonStruct.h), permettant à la bibliothèque dynamique et au programme principal de ne dépendre que de ce fichier de données pur.
Exemples de problèmes rencontrés lors de la programmation :
Problème 1 : Dépendance entre bibliothèques dynamiques et plugins Un plugin souhaite appeler l'interface DataExchangeGlobal::sendPluginMessage du module principal du programme, mais une erreur "non défini" s'affiche.
Solution : La cause fondamentale est que le plugin ne trouve pas la définition du symbole DataExchangeGlobal lors de sa compilation. Plusieurs raisons possibles :
- Déclaration d'en-tête non définie : Le plugin inclut seulement l'en-tête de
DataExchangeGlobal(déclaration), mais ne lie pas la bibliothèque dynamique contenant son implémentation, ce qui fait que l'éditeur de liens ne trouve pas le symbole. - Symbole non exporté :
DataExchangeGlobal, étant une classe de la bibliothèque dynamique du programme principal, n'utilise pasQ_DECL_EXPORTpour exporter son symbole, donc le plugin ne peut pas le reconnaître. - Mauvaise configuration du chemin de bibliothèque : Le projet du plugin n'a pas correctement configuré le chemin de la bibliothèque dynamique du programme principal (
LIBS), empêchant l'éditeur de liens de trouver le fichier de bibliothèque.
Concernant le troisième point, le programme principal où se trouve DataExchangeGlobal génère un fichier exécutable (.exe). Ainsi, pour qu'un plugin puisse lier sa bibliothèque dynamique, il faut scinder DataExchangeGlobal en une bibliothèque dynamique indépendante (bibliothèque partagée), servant de "dépendance commune" au programme principal et aux autres plugins.
Étape 1 : Dans Qt Creator, créez un nouveau projet, sélectionnez "Bibliothèque"→"Bibliothèque C++", type "Bibliothèque partagée" (bibliothèque dynamique), nommez-la DataExchangeLib;
Étape 2 : Copiez les fichiers (.h, .cpp) liés à DataExchangeGlobal du programme principal vers ce projet de bibliothèque dynamique.
Étape 3 : Modifiez l'en-tête de la bibliothèque dynamique (ajoutez les macros d'exportation / importation) : Pour que les classes de la bibliothèque dynamique puissent être accessibles par le programme principal et les plugins, vous devez marquer la classe avec les macros d'exportation Qt (pour assurer la compatibilité multiplateforme)
Étape 4 : Configurez le fichier de projet de la bibliothèque dynamique (.pro) :
QT += core # Ajoutez d'autres modules selon les besoins (comme network, gui)
TARGET = DataExchangeLib # Nom de la bibliothèque dynamique
CONFIG += shared # Génère une bibliothèque dynamique
DESTDIR = ../bin # La bibliothèque dynamique est générée dans le répertoire bin
Étape 5 : Les plugins ou le programme principal qui doivent accéder à DataExchangeGlobal doivent également lier cette bibliothèque dynamique dans leur fichier .pro :
LIBS += -L../bin -lDataExchangeLib # Répertoire de sortie et nom de la bibliothèque dynamique
Remarques importantes
- Ordre de compilation : Compilez d'abord
DataExchangeLibpour générer la bibliothèque dynamique, puis compilez le programme principal et les plugins (qui dépendent de la bibliothèque dynamique). - Dépendance à l'exécution : Assurez-vous que
DataExchangeLib.dllest dans le même répertoire que le programme principal.exeet les plugins.dll(ou dans un chemin que le système peut trouver), sinon une erreur "bibliothèque dynamique non trouvée" s'affichera.
Problème 2 : La bibliothèque dynamique (une interface de DataExchangeGlobal) a besoin d'accéder à l'interface serial du programme principal. Le programme principal dépend déjà de la bibliothèque dynamique, mais si la bibliothèque dynamique dépend à son tour du programme principal, une dépendance circulaire se produirait, entraînant un échec de compilation / d'édition des liens ou une anomalie de chargement à l'exécution.
Solution : Comme DataExchangeGlobal (bibliothèque dynamique) doit appeler SerialSingle/CanSingle du programme principal, mais ne peut pas dépendre directement de ces classes, il faut définir une interface de classe pure virtuelle correspondant aux fonctionnalités de SerialSingle/CanSingle, implémentée et enregistrée par le programme principal auprès de la bibliothèque dynamique. Grâce à la "couche d'abstraction d'interface", la bibliothèque dynamique appelle indirectement le programme principal tout en maintenant l'intégrité de la chaîne d'appels interne du programme principal.
Étape 1 : Dans DataExchangeLib, créez d'abord un fichier d'en-tête définissant une interface de classe pure virtuelle correspondant aux fonctionnalités de SerialSingle/CanSingle, servant de "contrat d'appel"
// Fichier ICommProxy.h dans DataExchangeLib
#ifndef ICOMMINTERFACE_H
#define ICOMMINTERFACE_H