La programmation de la structure du plug-in nécessite un conteneur de plug-in pour contrôler le fonctionnement de chaque DLL et organiser chaque sous-système divisé dans un fichier de bibliothèque DLL. Chaque programme DLL doit réserver des fonctions d'interface pour le conteneur. Généralement, les fonctions d'interface incluent : les fonctions qui commencent à appeler la bibliothèque DLL et les fonctions qui ferment la bibliothèque DLL. Grâce à la fonction d'interface, le conteneur de plug-in peut transmettre des paramètres au module DLL pour obtenir un contrôle dynamique. J'expliquerai les détails spécifiques de la mise en œuvre et donnerai le code de réponse ci-dessous.
Vous devrez peut-être d’abord comprendre la structure d’UNIT et la structure des projets dans DELPHI. Cet article ne traite pas en profondeur des détails théoriques de la programmation DLL, mais démontre seulement quelques codes pratiques. J'étudiais le livre "DELPHI In-owned Programming" de Liu Yi.
Je suis également dans la phase d'introduction de DELPHI. Je pense simplement qu'il y a certaines choses qui méritent d'être discutées dans ce développement de DLL, j'écris donc cet article et j'espère que vous pourrez donner des suggestions généreuses sur ce que je ne fais pas bien.
Introduction à l'exemple de programme
Afin de faciliter la lecture, j'utiliserai une partie du code de programme d'un système MIS pour démontrer certaines méthodes de programmation de plug-ins. L'exemple de programme est une application SGBD à structure C/S typique. Nous nous concentrerons sur les instructions de contrôle du programme-cadre (ci-après dénommé Hall) et le contrôle de réponse du programme de plug-in dll.
1. Structure du programme
Le conteneur de plug-ins Hall est créé à l'aide d'un projet indépendant. La fenêtre principale du Hall est équivalente au formulaire de conteneur MDI dans le programme MDI. Les fonctions d'interface dans la Dll seront explicitement appelées dans le Hall.
Chaque programme plug-in utilise son propre projet indépendamment. Contrairement aux projets ordinaires, le projet DLL crée un Dll Wizard et les fichiers générés par la compilation correspondante portent le suffixe DLL.
=550) window.open('/upload/20080315181507424.jpg');" src="http://files.VeVB.COm/upload/20080315181507424.jpg" onload="if(this.width>'550')this.width='550';if(this.height>'1000')this.height='1000';" border=0>
2. Conception des interfaces
Dans l'exemple de programme Narcissus, nous réservons deux fonctions d'interface :
AfficherDLLForm
Cette fonction transmet le handle de l'application à la fenêtre enfant de la DLL et le programme DLL créera dynamiquement une instance du formulaire DLL. Vous pouvez également transmettre une logique métier à la sous-fenêtre DLL sous forme de paramètres, tels que le nom du formulaire, le nom d'utilisateur actuellement connecté, etc. Utilisez cette fonction pour créer une instance de formulaire DLL lors de son premier appel.
FreeDLLForm
Cette fonction affichera la version de l'instance de fenêtre DLL et appellera la méthode FreeDLLForm de chaque formulaire DLL pour libérer l'instance créée à la sortie de l'application, sinon cela provoquera une erreur de lecture seule en mémoire. De même, vous pouvez également transmettre une certaine logique métier qui doit être effectuée lors de la publication du formulaire vers le formulaire DLL sous forme de paramètres.
3. Méthode de débogage
Le programme de formulaire DLL ne peut pas être exécuté directement et nécessite un conteneur de plug-in pour l'appeler. Par conséquent, nous devons d’abord implémenter un programme Hall de base, puis enregistrer Hall.exe dans un répertoire fixe. Définissez les paramètres suivants pour chaque projet DLL :
1) Ouvrez le projet DLL
2) Sélectionnez le menu Paramètres d'exécution
3) Accédez à notre conteneur Hall.exe dans la fenêtre contextuelle
De cette façon, le programme Hall sera automatiquement appelé lors du débogage du programme DLL, et le programme DLL sera débogué en utilisant l'interface d'appel réservée dans le Hall.
Implémentation de base du programme de plug-in
La méthode de conception du programme DLL n'est pas très différente de celle d'un WINAPP ordinaire, sauf que toutes les fenêtres sont enregistrées dans la bibliothèque DLL en tant que "ressource" spéciale et doivent être appelées manuellement, contrairement à WINAPP où les projets sont automatiquement créés. La méthode de déclaration des fonctions d'interface est très simple
1) Déclarez la fonction dans la section Implémentation de l'Unité
2) Ajoutez la marque stdcall à la fin de l'instruction de déclaration de fonction
3) Avant l'instruction de début du code du projet (Project View Source), utilisez l'instruction exports pour déclarer l'interface de fonction
Afin de rendre le code concis, j'aime personnellement ajouter une unité Unit (File New -- Unit) indépendamment dans le projet, puis définir tous les corps de fonction à afficher dans cette unité. N'oubliez pas d'utiliser également le. Unité du formulaire référencé. J'ai nommé cette unité UnitEntrance, initialisé la fenêtre à afficher dans la fonction ShowDLLForm et appelé la méthode Show pour l'afficher. HALL transmettra le nom d'utilisateur connecté en tant que paramètre. Après avoir obtenu le nom d'utilisateur, vous pouvez effectuer un contrôle d'autorisation. ce qui se reflète dans l'initialisation de l'interface supérieure.
Le code est le suivant
unité UnitOfficeEntrance ;
interface
utilise
Windows, messages, SysUtils, variantes, classes, graphiques, contrôles, formulaires ;
function ShowDLLForm(AHandle: THandle; ACaption: chaîne; AUserID: chaîne):boolean;stdcall;
function FreeDLLForm(AHandle: THandle; ACaption: chaîne; AUserID: chaîne):boolean;stdcall;
mise en œuvre
utilise UnitOfficialMainForm ; //Changement en unité de MAINFORM
var
DLL_Form:TFormOfficialMain; //Changement du nom de MAINFORM
//---------------------------------------------
//Nom : ShowDLLForm
//Func : le plug-in DLL appelle la fonction d'entrée
//Para : AHandle descripteur de programme attaché ; ACaption titre de ce formulaire
//Rtrn : N/A
//Authentification : CST
//Date : 2005-6-3
//---------------------------------------------
function ShowDLLForm(AHandle: THandle; ACaption: chaîne; AUserID: chaîne):booléen;
commencer
résultat :=vrai ;
essayer
Application.Handle:=AHandle; //Ancré au conteneur du programme principal
DLL_Form:=TFormOfficialMain.Create(Application); //Changement du NOM de MAINFORM
essayer
avec DLL_Form faire
commencer
Légende := ALégende ;
StatusBar.Panels.Items[0].Text := AUserID;
//Configurer l'interface utilisateur
Montrer ;
fin;
sauf
sur e:exception faire
commencer
dll_form.Free;
fin;
fin;
sauf
résultat :=faux ;
fin;
fin;
//---------------------------------------------
//Nom : FreeDLLForm
//Func : le plug-in DLL appelle la fonction d'exportation
//Para : AHandle descripteur de programme attaché
//Rtrn : vrai/faux
//Authentification : CST
//Date : 2005-6-11
//---------------------------------------------
function FreeDLLForm(AHandle: THandle; ACaption: chaîne; AUserID: chaîne):booléen;
commencer
Application.Handle:=AHandle; //Ancré au conteneur du programme principal
if DLL_Form.Showing then DLL_Form.Close; //Si la fenêtre est ouverte et fermée en premier, le déclenchement de FORM.CLOSEQUERY peut annuler le processus de fermeture
sinon DLL_Form.Affichage alors
commencer
DLL_Form.Free ;
résultat :=vrai ;
end //Toujours ouvert, indiquant CLOSEQUERY.CANCLOSE=FALSE
autre
commencer
résultat :=faux ;
fin;
fin;
fin.
Le code du fichier projet DLL est le suivant :
Responsable de la bibliothèque ;
{ Remarque importante sur la gestion de la mémoire des DLL : ShareMem doit être le
première unité de la clause USES de votre bibliothèque ET de votre projet (sélectionnez
Project-View Source) clause USES si votre DLL exporte des procédures ou
fonctions qui transmettent des chaînes en tant que paramètres ou résultats de fonction.
s'applique à toutes les chaînes transmises vers et depuis votre DLL, même celles qui
sont imbriqués dans des enregistrements et des classes. ShareMem est l'unité d'interface à utiliser.
le gestionnaire de mémoire partagée BORLNDMM.DLL, qui doit être déployé avec
avec votre DLL Pour éviter d'utiliser BORLNDMM.DLL, transmettez les informations de chaîne.
en utilisant les paramètres PChar ou ShortString }
utilise
SysUtils,
Cours,
UnitOfficialDetailForm dans 'UnitOfficialDetailForm.pas' {FormOfficialDetail},
UnitOfficialMainForm dans 'UnitOfficialMainForm.pas' {FormOfficialMain},
UnitOfficeEntrance dans 'UnitOfficeEntrance.pas',
UnitOfficialClass dans '../../Public/Library/UnitOfficialClass.pas',
UnitMyDataAdatper dans '../../Public/Library/UnitMyDataAdatper.pas',
UnitMyHeaders dans '../../Public/Library/UnitMyHeaders.pas';
{$R *.res}
exporte ShowDLLForm,FreeDLLForm ; //fonction d'interface
commencer
fin.
Une fois que le programme plug-in appelle la fenêtre DLL, l'instance de fenêtre restera au-dessus de la fenêtre HALL, vous n'avez donc pas à vous soucier de l'occlusion.
Mise en œuvre de programmes de conteneurs
1. Introduction des fonctions d'interface
Il existe deux manières d'appeler des fonctions dans la bibliothèque DLL : explicite et implicite. L'appel explicite est plus flexible, nous utilisons donc l'appel explicite. Dans Delphi, vous devez déclarer le type de fonction pour la fonction d'interface, puis instancier une instance du type de fonction. L'instance est en fait un pointeur vers la fonction, via le pointeur, nous pouvons accéder à la fonction, transmettre des paramètres et obtenir. la valeur de retour. Ajoutez la déclaration de la classe de fonction dans la section Interface du fichier unité :
taper
//Définissez le type de fonction d'interface, la fonction d'interface provient de l'interface DLL
TShowDLLForm = Function(AHandle:THandle; ACaption: String; AUserID:string):Boolean;stdcall;
TFreeDLLForm = Function(AHandle:THandle; ACaption: String; AUserID:string):boolean;stdcall;
Afficher les fonctions de bibliothèque d'appel nécessite les étapes suivantes :
1) Charger le fichier de bibliothèque DLL
2) Obtenir l'adresse de la fonction
3) Exécuter la fonction
4) Libérez la bibliothèque DLL
Nous discuterons ensuite de ces étapes en détail.
2. Chargez le fichier de bibliothèque DLL
La bibliothèque DLL peut être chargée en mémoire en appelant la fonction API LoadLibrary. Nous ne discuterons pas ici de l'impact des DLL sur la gestion de la mémoire. Le paramètre de LoadLibrary est le chemin d'adresse du fichier DLL. Si le chargement réussit, une variable de type CARDINAL sera renvoyée comme handle de la bibliothèque DLL si le fichier cible n'existe pas ou si d'autres raisons provoquent le chargement de la DLL ; fichier échoue, un 0 sera renvoyé.
3. Instancier les fonctions d'interface
La fonction API pour obtenir le pointeur de fonction d'interface est GetProcAddress (descripteur de fichier de bibliothèque, nom de fonction). Si la fonction est trouvée, le pointeur de la fonction sera renvoyé. En cas d'échec, NIL sera renvoyé.
Définissez une variable de pointeur de fonction à l'aide du type de fonction défini ci-dessus, puis utilisez l'opérateur @ pour obtenir l'adresse de la fonction, afin de pouvoir utiliser la variable de pointeur pour accéder à la fonction. Le code principal est le suivant :
…
var
ShowDLLForm : TShowDLLForm ; // Instance de fonction d'interface DLL
FreeDLLForm : TFreeDLLForm ;
commencer
essayer
commencer
APlugin.ProcAddr := LoadLibrary(PChar(sPath));
APlugin.FuncFreeAddr := GetProcAddress(APlugin.ProcAddr,'FreeDLLForm');
APlugin.FuncAddr := GetProcAddress(APlugin.ProcAddr,'ShowDLLForm');
@ShowDLLForm:=APlugin.FuncAddr;
@FreeDLLForm:=APlugin.FuncFreeAddr;
si ShowDllForm (Self.Handle, APlugin.Caption, APlugin.UserID) alors
Résultat :=Vrai
…
4. Une méthode de mise en œuvre spécifique
Afin de gérer les plug-ins de manière structurée et de faciliter l'expansion future du système, nous pouvons combiner les informations DLL disponibles enregistrées dans la base de données, puis accéder dynamiquement au programme DLL en interrogeant les enregistrements de la base de données.
1) Conception de la table du module système
Pour les systèmes MIS, vous pouvez utiliser les conditions DBS existantes pour établir une table de modules système afin d'enregistrer les fichiers DLL et les informations associées mappées aux modules système.
Type de rôle de nom de champ
Index d'identification automatiqueINT
modAlias alias du module VARCHAR
nom du module modName VARCHAR
modWndClass formulaire identifiant unique VARCHAR
chemin de la DLL modFile VARCHAR
modMemo note TEXTE
・Les alias de module sont utilisés pour unifier les règles de dénomination pendant la phase de conception de la programmation, en particulier pour la référence des membres de l'équipe pendant le développement de l'équipe.
・Le nom du module sera transmis comme paramètre ACAPTION à la fonction SHOWDLLFORM comme titre de la fenêtre DLL.
・L'identifiant unique du formulaire est le CLASSNAME de la fenêtre principale dans le sous-module DLL, qui est utilisé pour déterminer la fenêtre à contrôler au moment de l'exécution.
・Le chemin DLL enregistre le nom du fichier DLL, qui sera converti en chemin absolu dans le programme.
2) Structure des données d'informations du plug-in
La définition d'une interface de données qui enregistre les informations relatives aux plug-ins peut contrôler de manière centralisée les plug-ins DLL. Ajoutez le code suivant à la section Interface :
taper
//Définir la classe d'informations du plug-in
TMyPlugins = classe
Légende : Chaîne ; //Titre du formulaire DLL
DllFileName : String ; //Chemin du fichier DLL
WndClass:String; //Identification du formulaire
ID utilisateur : chaîne ; //Nom d'utilisateur
ProcAddr:THandle; // Descripteur de bibliothèque chargé par LOADLIBRARY
FuncAddr:Pointeur; //Pointeur de fonction SHOWDLLFORM
FuncFreeAddr:Pointer; // Pointeur de fonction FREEDLLFORM
fin;
…
Créez une instance de TMyPlugins pour chaque plug-in. Les méthodes d'initialisation de ces instances seront décrites ci-dessous.
3) Fonction de chargement de plug-in
Dans cet exemple, la fenêtre DLL est chargée et affichée dans le cas qui déclenche l'ouverture de la fenêtre fille dans HALL. Une fois l'événement de bouton déclenché, déterminez d'abord si la DLL a été chargée en fonction de l'instance de structure du plug-in. Si elle a été chargée, contrôlez l'affichage ou la fermeture de la fenêtre si elle n'est pas chargée, accédez à la table de données et. attribuez les champs à la structure du plug-in, puis exécutez le chargement. Obtenir le pointeur fonctionne.
Le code partiel est le suivant
…
//---------------------------------------------
//Nom : OpenPlugin
//Func : Processus de contrôle des informations du plug-in : Initialisation==》Définir les autorisations==》Fenêtre de chargement de la DLL
//Para : APlugin-TMyPlugins ; alias sAlias ;
//Rtrn : N/A
//Authentification : CST
//Date : 2005-6-2
//---------------------------------------------
procédure TFormHall.OpenPlugin(AFromActn: TAction ;APlugin:TMyPlugins; sAlias:string; sUserID:string);
var hWndPlugin:HWnd;
commencer
//Détermine si la fenêtre du plug-in a été chargée hWndPlugin:=FindWindow(PChar(APlugin.WndClass),nil);
if hWndPlugin <> 0 then //La fenêtre du plug-in a été chargée
commencer
sinon IsWindowVisible(hWndPlugin) alors
commencer
AFromActn.Checked := True ;
ShowWindow(hWndPlugin,SW_SHOWDEFAULT); //Affichage
fin
autre
commencer
AFromActn.checked := False;
ShowWindow(hWndPlugin,SW_HIDE);
fin;
Quitter ; //Quitter le processus de création du plug-in
fin;
//Initialiser l'instance de classe de plug-in
sinon InitializeMyPlugins(APlugin,sAlias) alors
commencer
showmessage('Erreur lors de l'initialisation de la classe du plug-in.');
sortie;
fin;
//Obtenir la valeur d'autorisation actuelle
APlugin.UserID := sUserID;
//Fenêtre de chargement de la DLL
sinon LoadShowPluginForm(APlugin) alors
commencer
showmessage('Erreur lors du chargement du plug-in du centre.');
sortie;
fin;
fin;
//---------------------------------------------
//Nom : InitialiserMesPlugins
//Func : initialiser l'instance MYPLUGIN (Caption | DllFileName | IsLoaded)
//Para : APlugin-TMyPlugins
//Rtrn : N/A
//Authentification : CST
//Date : 2005-6-2
//---------------------------------------------
function TFormHall.InitializeMyPlugins(APlugin:TMyPlugins; sAlias:String):Boolean;
var
strSQL:chaîne;
monDA:TMyDataAdapter;
commencer
Résultat :=Faux ;
monDA:=TMyDataAdapter.Create;
strSQL:='SELECT * FROM SystemModuleList WHERE modAlias='+QuotedStr(sAlias);
essayer
monDA.RetrieveData(strSQL);
sauf
sur E:Exception faire
commencer
résultat :=faux ;
monDA.Free ;
sortie;
fin;
fin;
essayer
commencer
avec myDA.MyDataSet faire
commencer
si ce n'est pas IsEmpty alors
commencer
APlugin.Caption:= FieldByName('modName').Value;
APlugin.DllFileName := FieldByName('modFile').Value;
APlugin.WndClass := FieldByName('modWndClass').Value;
résultat :=Vrai ;
fin;
Fermer;
end; //fin de avec...faire...
fin; //fin de l'essai
sauf
sur E:Exception faire
commencer
Résultat :=Faux ;
monDA.Free;
Sortie;
fin; //fin de l'exception
end; //fin de l'essai...sauf
monDA.Free;
fin;
//---------------------------------------------
//Nom : LoadShowPluginForm
//Func : Charge le plug-in DLL et affiche la fenêtre
//Para : APlugin-TMyPlugins
//Rtrn : vrai-créé avec succès
//Authentification : CST
//Date : 2005-6-2
//---------------------------------------------
fonction TFormHall.LoadShowPluginForm (const APlugin:TMyPlugins):boolean;
var
ShowDLLForm : TShowDLLForm ; // Instance de fonction d'interface DLL
FreeDLLForm : TFreeDLLForm ;
sPath:string; //Chemin complet du fichier DLL
commencer
essayer
commencer
sPath :=ExtractFilepath(Application.ExeName)+ 'plugins/' + APlugin.DllFileName ;
APlugin.ProcAddr := LoadLibrary(PChar(sPath));
APlugin.FuncFreeAddr := GetProcAddress(APlugin.ProcAddr,'FreeDLLForm');
APlugin.FuncAddr := GetProcAddress(APlugin.ProcAddr,'ShowDLLForm');
@ShowDLLForm:=APlugin.FuncAddr;
@FreeDLLForm:=APlugin.FuncFreeAddr;
si ShowDllForm (Self.Handle, APlugin.Caption, APlugin.UserID) alors
Résultat :=Vrai
autre
Résultat :=Faux ;
fin;
sauf
sur E:Exception faire
commencer
Résultat :=Faux ;
ShowMessage('Erreur lors du chargement du module plug-in, veuillez vérifier si les fichiers du répertoire PLUGINS sont complets.');
fin;
fin;
fin;
…
4) Contrôle de la fenêtre DLL
Comme l'illustre le code en 3), l'ouverture et la fermeture de la fenêtre DLL se font uniquement au niveau de la couche de présentation. La fermeture de la fenêtre ne libère pas réellement la fenêtre DLL. Elle appelle simplement la fonction API FindWindow pour obtenir le handle de formulaire en fonction de la fenêtre. identifiant (c'est-à-dire Form.name). Utilisation Le paramètre nCmdShow de la fonction SHOWWINDOW contrôle l'affichage/le masquage de la fenêtre.
En fait, c'est un point faible dans l'implémentation de mon programme. Si la méthode Self.close est utilisée dans la fenêtre DLL, cela provoquera une erreur de mémoire. Il n'y a aucun moyen de le résoudre en raison de capacités limitées, c'est donc le cas. le dernier recours. Par conséquent, le bouton de fermeture de la fenêtre principale de chaque programme DLL doit être masqué. :-P
5) Sortie de la bibliothèque DLL
Lorsque le programme se termine, la bibliothèque DLL doit être libérée une par une en fonction de l'instance d'informations du plug-in. La fonction pour libérer la bibliothèque DLL est la suivante :
procédure TFormHall.ClosePlugin(aPLG:TMyPlugins);
var
FreeDLLForm:TFreeDLLForm;
commencer
si aPLG.ProcAddr = 0 alors quittez ;
si aPLG.FuncFreeAddr = nil alors quittez ;
@FreeDLLForm :=aPLG.FuncFreeAddr ;
sinon FreeDLLForm(Application.Handle,'','') alors
showMessage('err');
fin;
résumé
L'effet courant de cet exemple de programme est le suivant :
=550) window.open('/upload/20080315181507979.jpg');" src="http://files.VeVB.COm/upload/20080315181507979.jpg" onload="if(this.width>'550')this.width='550';if(this.height>'1000')this.height='1000';" border=0>
Parmi les méthodes ci-dessus, parce qu'il existe de nombreux problèmes non résolus en raison de capacités limitées, j'ai adopté certaines méthodes de dissimulation qui ne semblent pas raisonnables. J'espère que tout le monde pourra concevoir une meilleure solution après avoir essayé un peu. un excellent moyen d'en apprendre davantage.