Implémentation de fenêtres normales à Delphi
Résumé Dans la bibliothèque VCL de Delphi, pour la commodité d'utilisation et de l'implémentation, l'application d'objet d'application crée une fenêtre cachée pour le traitement des réponses des messages. C'est cette fenêtre qui fait que les programmes développés avec VCL semblent quelque peu déformés, comme ne pas pouvoir organiser et carreler normalement avec d'autres fenêtres. Grâce à une analyse approfondie de VCL, cet article fournit une solution qui peut résoudre le problème en modifiant uniquement 3 lignes de code dans le fichier du projet d'application, sans aucune modification de la méthode de programmation d'origine.
Mot-clé VCL, fenêtre normale, normalisation
1 Introduction
Les applications Windows écrites dans la bibliothèque de classe VCL fournie par Delphi ont une fonctionnalité distincte qui est évidemment différente de la fenêtre Windows standard - le menu système de la fenêtre principale est différent du menu système de la barre des tâches. D'une manière générale, le menu système dans la fenêtre principale a six éléments de menu, tandis que le menu du système TaskBar ne comporte que trois éléments de menu. En usage réel, nous avons constaté que les programmes développés avec VCL ont l'embarras suivant:
1) Pas assez beau. Ceci est certain, s'il ne correspond pas aux normes, il apparaîtra naturellement un peu déformé.
2) Il n'y a pas d'effet d'animation lorsque la fenêtre principale est minimisée.
3) La fenêtre ne peut pas être disposée et les carreaux normalement avec d'autres fenêtres.
4) Le menu du système de barre des tâches a la priorité la plus élevée. En présence d'une fenêtre modale, l'ensemble du programme peut toujours être minimisé, contrairement à la conception de la fenêtre modale.
Le problème de la minimisation des effets d'animation dans la fenêtre principale a été résolu par la fonction showwinnoanima dans les formulaires. Bien que cela n'aura aucun impact sur l'application dans la plupart des cas, il est en effet inacceptable dans certaines situations où les effets professionnels sont poursuivis. Étant donné que C ++ Builder et Delphi utilisent le même ensemble de bibliothèques de classe, les problèmes ci-dessus existent également dans les applications Windows écrites à l'aide de C ++ Builder.
J'ai discuté de cette question dans les articles précédents (se trouvent chez Forrest Gump), et le récit à l'époque semblait essentiellement une astuce, et j'ai trouvé cette méthode par hasard. La tâche de cet article est d'analyser la bibliothèque de classe VCL pour expliquer le principe de le faire, puis de donner une méthode qui n'utilise que 3 lignes de code pour résoudre complètement le problème de cette "fenêtre anormale" à Delphi.
2 principe
2.1 Processus de création d'applications
Voici un fichier de projet Delphi d'application typique.
Project Project1;
usages
Formes,
Unit1 dans 'Unit1.pas' {Form1};
{$ R * .res}
Commencer
Application.Initialize;
Application.CreateForm (TForm1, Form1);
Application.run;
fin .
La fenêtre cachée est créée par l'objet d'application, alors d'où vient l'objet d'application? Appuyez et maintenez Ctrl dans la fenêtre d'édition de code de Delphi et cliquez sur l'application, vous constaterez que l'objet d'application est l'un des nombreux objets globaux définis dans l'unité Forms.PAS. Cela ne suffit pas, ce que nous voulons savoir, c'est où l'objet d'application a été créé, car une instance de la classe Tapplication doit être créée avec succès avant de pouvoir nous y référer.
Pensez-y, y a-t-il un code qui sera exécuté avant l'application. Soit dit en passant, il s'agit du code du segment de code d'initialisation. Après avoir débogué soigneusement le code source VCL, vous pouvez savoir que de nombreuses unités dans VCL ont des segments de code d'initialisation. Toutes les actions d'initialisation.
En recherchant dans le répertoire de code source VCL avec le mot clé "tapplication.create", nous avons trouvé le code pour créer l'objet d'application dans l'unité Controls.PAS. Dans le segment de code d'initialisation de l'unité Controls.PAS, il y a un appel à la procédure InitControls, et l'implémentation d'InitControls est la suivante:
Commandes unitaires ;
…
Initialisation
...
InitControls;
Procédure InitControls;
Commencer
...
Souris: = tmouse.create;
Écran: = tscreen.create ( nil );
Application: = tapplication.create ( nil );
...
fin ;
OK, à ce stade, notre analyse a terminé la première étape, car pour résoudre le problème des fenêtres anormales, nous devons faire une chose avant d'initialiser l'objet d'application, il est donc très important de comprendre le processus d'initialisation de l'application.
2.2 Variables ISLibrary
La variable IsLibrary est l'une des variables de drapeau global définies dans l'unité System.PAS. Si la valeur d'IsLibrary est vraie, cela signifie que le module de programme est une bibliothèque de liens dynamiques, sinon c'est un programme exécutable. Certains processus de la bibliothèque de classe VCL complètent différentes actions en fonction de différentes valeurs de cette variable de drapeau. Autrement dit, cette variable joue un rôle clé dans la résolution du problème de fenêtre anormal de Delphi.
Comme mentionné précédemment, pour plus de commodité, une fenêtre invisible a été créée lorsque l'objet d'application a été initialisé (c'est-à-dire la fenêtre avec "Tapplication" comme le nom de classe vu avec des outils comme Spy ++), mais c'est aussi à cause de cette invisible que la fenêtre rend Les programmes développés avec Delphi montrent de nombreuses anomalies. OK, si nous pouvons supprimer cette fenêtre invisible (et supprimer le menu du système de barre des tâches en même temps) et le remplacer par notre fenêtre principale d'application, tous les problèmes ne seraient-ils pas résolus?
C'est simple à dire, mais nécessite-t-il une chirurgie majeure pour mettre en œuvre le code source VCL? Cela ne serait-il pas un peu de mettre la charrette devant le cheval? Bien sûr, la réponse est non, sinon cet article n'aurait pas été disponible. Ce que je veux dire ici, c'est que dans la prochaine analyse, nous verrons que la soi-disant "la façon de la programmation réside dans un esprit", la pratique de la plantation de saules involontaire dans la conception de tapplication nous laisse en fait la solution à ce problème . Si vous ne faites pas d'analyse de code source, vous devrez peut-être faire le tour en rond, mais en fait, nous verrons que la conception de génie ne nous laisse pas plus ou moins, juste.
Ouvrez la création du constructeur de la classe Tapplication et nous trouverons une telle ligne de code.
Constructor Tapplication.Create (Aowner: TComponent);
Commencer
...
Si ce n'est pas IsLibrary , créez-vous;
...
fin ;
Ce qui est dit ici, c'est que si le module de programme n'est pas une bibliothèque de liens dynamiques, exécutez CreateHandle et que le travail effectué par CreateHandle est le suivant dans l'aide: "S'il n'y a pas de fenêtre d'application, créez un programme", ici " La fenêtre "est la fenêtre invisible mentionnée ci-dessus, qui est le coupable. Dans la classe Tapplication, la variable Fhandle est utilisée pour enregistrer sa poignée de fenêtre. Il s'agit de compléter différentes actions en fonction de la valeur d'IsLibrary, car dans les bibliothèques de liens dynamiques, les boucles de message ne sont généralement pas requises, mais pour utiliser des objets d'application pour développer des bibliothèques de liens dynamiques avec VCL, alors voici la conception. Ok, nous avons juste besoin de tromper l'objet d'application, d'attribuer IsLibrary à true avant sa création, et de filtrer l'exécution de CreateHandle et de supprimer cette fenêtre ennuyeuse.
Le code attribué à IsLibrary doit évidemment être placé dans le segment de code d'initialisation d'une certaine unité. L'objet d'application est créé, dans le fichier du projet, nous devons placer l'unité contenant le code d'attribution avant l'unité des formulaires, comme suit (en supposant que l'unité est nommée unitdllex.pas):
modèle de programme ;
usages
Unitdllex dans 'unitdllex.pas',
Formes,
Formmain dans 'formmain.pas' {mainform},
...
La liste des codes unitdllexe.pas est la suivante:
unité unitdllexe;
interface
Mise en œuvre
Initialisation
IsLibrary: = true;
// Dites à l'objet Applciation qu'il s'agit d'une bibliothèque de liens dynamiques et n'a pas besoin de créer des fenêtres cachées.
fin .
D'accord, compilez et exécutez-le. fenêtres. Mais le problème est que la fenêtre ne peut pas être minimisée. Que se passe-t-il? C'est toujours l'ancienne, suivez-le.
2.3 Minimisation des fenêtres principales
La minimisation appartient aux commandes système, et en fin de compte, elle doit être appelée la fonction API DefwindowProc pour minimiser la fenêtre, nous avons donc trouvé la fonction wmsyscommand dans tCustomForm qui répond au message WM_SYSCOMAND sans aucune difficulté, qui écrit clairement pour rediriger le message minimisée à l'application .Wndproc à gérer:
Procédure tCustomForm.WmsySCommand (Message var : twmsyscommand);
Commencer
avec un message faire
Commencer
if (cmdType et $ fff0 = sc_minimize) et (application.mainform = self) puis
Application.wndproc (tMessage (message))
...
fin ;
fin ;
Dans Application.wndProc, la méthode d'application minimiser est appelée en réponse au message minimisée, de sorte que le nœud du problème doit être dans le processus minimiser.
Procédure tapplication.wndproc (message var : tMessage);
...
Commencer
...
avec un message faire
cas msg de
Wm_syscommand:
cas wparam et $ fff0 de
Sc_minimize: minimiser;
SC_RESTORE: restaurer;
autre
Défaut;
...
fin ;
Enfin, trouvez tapplication.mimize et vous comprendrez tout. L'appel à la fonction DefwindowProc ici ne produit aucun effet, pourquoi? Étant donné que nous avons déjà trompé l'objet de demande, filtré l'appel de CreateHandle et n'a pas créé la fenêtre nécessaire pour répondre au message de l'objet d'application, la poignée Fhandle est 0, et l'appel est bien sûr sans succès. Si vous pouvez pointer Fhandle vers notre fenêtre d'application principale, cela résoudra le problème.
procédure tapplication.minimize;
Commencer
...
DefwindowProc (fhandle, wm_syscommand, sc_minimize, 0);
// La valeur fhandle ici est 0
...
fin ;
3 implémentation
La conception involontaire des génies de Borland nous a une fois de plus permis de résoudre le problème. D'après l'analyse précédente, nous savons que dans la bibliothèque de liens dynamiques développée avec VCL, il n'y a pas de fenêtre cachée pour recevoir des messages Windows (CreateHandle ne s'exécute pas), mais dans la bibliothèque de liens dynamiques, si vous souhaitez afficher la fenêtre, vous avez besoin une fenêtre parent. Comment résoudre ce problème? Le concepteur VCL conçoit la variable Fhandle qui contient des poignées de fenêtre invisibles comme écrivains, nous pouvons donc simplement attribuer une valeur à Fhandle pour fournir une fenêtre parent pour la fenêtre enfant qui doit être affichée. Par exemple, dans un plug-in de bibliothèque de liens dynamiques pour afficher un formulaire, nous passons généralement la poignée de l'objet d'application via une fonction de la bibliothèque de liens dynamiques dans le fichier exécutable du module principal et l'attribuez à l'application.Handle de la dynamique Bibliothèque de liens dans le fichier exécutable du module principal et attribuez-le à l'application.Handle de la bibliothèque de liens dynamiques dans l'application.
Procédure setApplicationHandle (mainAppwnd: hwnd)
Commencer
Application.handle: = mainAppwnd;
fin ;
Ok, puisque l'aplication.handle n'est en fait qu'une poignée de fenêtre utilisée en interne pour répondre aux messages, et la fenêtre invisible qui aurait dû être créée a été supprimée par nous, nous devons seulement donner une poignée de fenêtre pour remplacer qui n'est pas Il suffit de cacher la poignée de la fenêtre à l'origine inutile? Où puis-je trouver une telle fenêtre? La fenêtre principale de l'application est le meilleur choix, donc le code suivant est disponible.
modèle de programme ;
usages
Unitdllex dans 'unitdllex.pas',
Formes,
Formmain dans 'formmain.pas' {mainform};
{$ R * .res}
Commencer
Application.Initialize;
Application.CreateForm (TFormMain, formMain);
Application.handle: = formmain.handle;
Application.run;
fin .
Ainsi, tous les problèmes ont été résolus. Vous n'avez pas besoin de modifier le code source VCL, et vous n'avez pas besoin de modifier le programme d'origine. de trois lignes de code pour faire de votre fenêtre d'application est exactement aussi normale que n'importe quelle fenêtre Windows standard.
1) La barre des tâches et la barre de titre de fenêtre ont des menus système cohérents.
2) Il y a un effet d'animation lorsque la fenêtre principale est minimisée.
3) La fenêtre peut être disposée et carrelée normalement avec d'autres fenêtres.
4) Lorsqu'il y a une fenêtre modale, il ne peut pas fonctionner sur sa fenêtre parent.
Le code d'implémentation ci-dessus est utilisé dans toutes les versions de Delphi.