web-dev-qa-db-fra.com

Delphi: Qu'est-ce que Application.Handle?

Quel est TApplication.Handle?

  • D'où est ce que ça vient?
  • Pourquoi existe-t-il?
  • Et le plus important: pourquoi tous les formulaires l'ont comme poignée de fenêtre parent?

L'aide Delphi dit:

TApplication.Handle

Donne accès au handle de fenêtre du formulaire principal (fenêtre) de l'application.

property Handle: HWND;

Description

Utilisez Handle lors de l'appel de fonctions API Windows qui nécessitent un handle de fenêtre parent. Par exemple, un DLL qui affiche ses propres fenêtres contextuelles de niveau supérieur a besoin d'une fenêtre parent pour afficher ses fenêtres dans l'application. L'utilisation de la propriété Handle fait de ces fenêtres une partie de l'application, donc qu'ils sont réduits, restaurés, activés et désactivés avec l'application.

Si je me concentre sur les mots " le handle de fenêtre du formulaire principal de l'application", et je suppose que cela signifie le handle de fenêtre du formulaire principal de l'application, alors je peux comparer:

  • "le handle de fenêtre du formulaire principal de l'application", avec
  • le handle de fenêtre du MainForm du Application

mais ce ne sont pas les mêmes:

Application.MainForm.Handle: 11473728
Application.Handle: 11079574

Donc qu'est-ce Application.Handle?

  • D'où est ce que ça vient?
  • De quelle poignée de fenêtre Windows® s'agit-il?
  • Si est le handle de fenêtre Windows® des Application des MainForm, alors pourquoi ne correspondent-ils pas?
  • Si ce n'est pas le handle de fenêtre du ApplicationMainForm, alors qu'est-ce que c'est?
  • Plus important encore: pourquoi est-ce l'ultime parent propriétaire de chaque formulaire?
  • Et le plus important: pourquoi tout se détraque si j'essaie d'avoir une forme sans parenté sans propriétaire (donc il peut apparaître sur la barre des tâches), ou essayez d'utiliser quelque chose comme IProgressDialog ?

Vraiment ce que je demande, c'est: quelle est la logique de conception qui fait que Application.Handle existe? Si je peux comprendre le pourquoi, le comment devrait devenir évident.


Mise à jour Comprendre à travers un jeu de vingt questions:

En parlant de la solution de faire apparaître une fenêtre dans la barre des tâches en faisant son propriétaire null, Peter ci-dessous en 2000 a dit :

Cela peut entraîner certains problèmes avec les formulaires modaux affichés à partir des formulaires secondaires.

Si l'utilisateur quitte l'application pendant qu'un formulaire modal est en place, puis revient au formulaire qui l'a montré, le formulaire modal peut se cacher sous le formulaire. Il est possible de résoudre ce problème en s'assurant que la forme modale est parentée [sic; il voulait dire qu'il appartenait] au formulaire qui le montrait (en utilisant params.WndParent comme ci-dessus)

Mais cela n'est pas possible avec les dialogues standard de l'unité Dialogs et les exceptions , qui nécessitent plus d'efforts pour les faire fonctionner correctement ( manipulant essentiellement Application.OnActivate, en recherchant les formes modales parentées à l'application via GetLastActivePopup et en les amenant en haut de l'ordre Z via SetWindowPos).

  • Pourquoi une forme modale se retrouve-t-elle coincée derrière d'autres formes?
  • Quel mécanisme amène normalement une forme modale à l'avant, et pourquoi n'est-il pas fonctionnel ici?
  • Windows® est responsable de l'affichage des fenêtres empilées. Qu'est-ce qui ne va pas si Windows® n'affiche pas les bonnes fenêtres?

Il a également parlé de l'utilisation du nouveau style étendu de Windows qui force une fenêtre à apparaître dans la barre des tâches (lorsque les règles normales pour la rendre non possédée sont insuffisantes, peu pratiques ou indésirables), en ajoutant le WS_EX_APPWINDOW style étendu:

procedure TForm2.CreateParams(var Params: TCreateParams); 
begin 
   inherited CreateParams( params ); 

   Params.ExStyle := Params.ExStyle or WS_EX_APPWINDOW; 
end; 

Mais il met en garde:

Si vous cliquez sur un bouton de la barre des tâches des formulaires secondaires alors qu'une autre application est active, tous les formulaires d'applications seront affichés en premier. Si vous ne voulez pas qu'il y ait d'option

Qui amène tous les formulaires au premier plan alors que le propriétaire du formulaire est toujours Application.Handle. Est-ce que Application fait cela? Pourquoi ça fait ça? Plutôt que de faire cela, ne devrait-il pas ne pas le faire? Quel est l'inconvénient de ne pas faire cela; je vois l'inconvénient de le faire (les menus du système ne fonctionnent pas correctement, les vignettes des boutons de la barre des tâches sont inexactes, Windows® Shell ne peut pas minimiser les fenêtres.


Dans un autre article traitant de Application, Mike Edenfield dit que la fenêtre parente envoie les autres fenêtres à leurs messages minimiser, maximiser et restaurer :

Cela ajoutera le bouton de la barre des tâches pour votre formulaire, mais il y a quelques autres détails mineurs à gérer. De toute évidence, votre formulaire reçoit toujours minimiser/maximiser qui est envoyé au formulaire parent (le formulaire principal de la demande). Afin d'éviter cela, vous pouvez installer un gestionnaire de messages pour WM_SYSCOMMAND en ajoutant une ligne telle que:

procedure WMSysCommand(var Msg: TMessage); WM_SYSCOMMAND; 

procedure TParentForm.WMSysCommand(var Msg: TMessage); 
begin 
   if Msg.wParam = SC_MINIMIZE then 
   begin 
      // Send child windows message, don't 
      // send to windows with a taskbar button. 
   end; 
end; 

Notez que ce gestionnaire se présente sous la forme [~ # ~] parent [~ # ~] de celui dont vous souhaitez vous comporter indépendamment> du reste de l'application, afin d'éviter de transmettre le message de minimisation. Vous pouvez ajouter un code similaire pour SC_MAXIMIZE, SC_RESTORE, etc.

Comment se fait-il que les messages de minimisation/maximisation/restauration pour mes fenêtres Windows® ne vont pas dans ma fenêtre? Est-ce parce que des messages destinés à une fenêtre sont envoyés par Windows® au propriétaire de la fenêtre? Et dans ce cas, tous les formulaires d'une application Delphi sont "possédés" par Application? Cela ne signifie-t-il pas que la nullité du propriétaire:

procedure TForm2.CreateParams(var Params: TCreateParams);
begin
   inherited;
   Params.WndParent := 0; //NULL
end;

supprimera Application et son Window Handle n'interfère pas avec mon formulaire, et Windows devrait à nouveau envoyer moi mes messages de minimisation/maximisation/restauration?


Peut-être que si nous comparions et contrastions maintenant une application Windows "normale" fait des choses, avec la façon dont Borland a initialement conçu les applications Delphi pour faire les choses - en ce qui concerne cet objet Application et sa boucle principale.

  • quelle solution a été la résolution d'objet Application?
  • Quelle modification a été apportée aux versions ultérieures de Delphi afin que ces mêmes problèmes n'existent pas?
  • Le changement dans les versions ultérieures de Delphi n'a-t-il pas introduit d'autres problèmes, que la conception initiale de l'application a tant essayé de résoudre?
  • Comment ces applications plus récentes peuvent-elles encore fonctionner sans que l'application ne les interfère?

De toute évidence, Borland a réalisé la faille dans leur conception initiale. Quelle était leur conception initiale, quel problème a-t-il résolu, quel est le défaut, quelle a été la nouvelle conception et comment résout-il le problème?

49
Ian Boyd

La raison de la fenêtre d'application a un peu d'histoire sordide. Lors du développement de Delphi 1, nous savions que nous voulions utiliser le modèle d'interface utilisateur "SDI" (fenêtres dispersées sur tout le bureau) pour l'IDE. Nous savions également que Windows aspirait (et continue de faire) à ce modèle. Cependant, nous avons également remarqué que Visual Basic à l'époque utilisait ce modèle et qu'il semblait bien fonctionner. Après un examen plus approfondi, nous avons constaté que VB a utilisé une fenêtre de stationnement "cachée" spéciale qui a été utilisée comme "propriétaire" (Windows brouille parfois la notion de parent et de propriétaire, mais la distinction est similaire en VCL) pour toutes les autres fenêtres visibles.

C'est ainsi que nous avons résolu le "problème" où les fenêtres contenant le menu principal étaient rarement focalisées, donc le traitement Alt-F pour le menu Fichier ne fonctionnerait tout simplement pas. En utilisant cette fenêtre de stationnement centrale comme intermédiaire, nous pourrions plus facilement suivre et acheminer les messages vers les fenêtres appropriées.

Cet arrangement a également résolu un autre problème où normalement plusieurs fenêtres de niveau supérieur étaient entièrement indépendantes. En faisant en sorte que l'application gère le "propriétaire" de toutes ces fenêtres, elles se comporteraient toutes de concert. Par exemple, vous avez peut-être remarqué que lorsque vous sélectionnez any des fenêtres d'application, all les fenêtres d'application se déplacent vers l'avant et conservent leur ordre z les unes par rapport aux autres. Cela permettrait également de réduire et de restaurer l'application en tant que regroupement fonctionnel.

C'est une conséquence de l'utilisation de ce modèle. Nous pourrions avons fait manuellement tout ce travail pour garder les choses droites, mais la philosophie de conception était de ne pas réinventer Windows, mais de l'exploiter là où nous le pouvions. C'est aussi pourquoi un TButton ou un TEdit est vraiment une classe et un style de fenêtre Windows "User" BUTTON et EDIT, respectivement.

Au fur et à mesure que Windows évoluait, ce modèle "SDI" a commencé à tomber en disgrâce. En fait, Windows lui-même a commencé à devenir "hostile" à ce style d'application. Depuis Windows Vista et jusqu'à 7, le shell utilisateur ne semble pas bien fonctionner avec une application utilisant une fenêtre de parking. Nous avons donc décidé de mélanger les choses dans VCL pour éliminer la fenêtre de stationnement et déplacer sa fonction dans le formulaire principal. Cela présentait plusieurs problèmes de "poule et oeuf" par lesquels nous devons avoir la fenêtre de stationnement disponible assez tôt dans l'initialisation de l'application pour que d'autres fenêtres puissent "s'y attacher", mais le formulaire principal lui-même pourrait ne pas être construit assez tôt. TApplication doit sauter à travers quelques cerceaux pour que cela fonctionne, et il y a eu quelques cas subtils d'Edge qui ont causé des problèmes, mais la plupart des problèmes ont été résolus. Cependant, pour toute application que vous avancerez, elle continuera d'utiliser l'ancien modèle de fenêtre de stationnement.

49
Allen Bauer

Toutes les applications VCL ont une fenêtre de niveau supérieur "cachée" appelée Application. Il est créé automatiquement au démarrage de l'application. Il s'agit, entre autres, du principal gestionnaire de messages Windows pour VCL - d'où Application.ProcessMessages.

Le fait de masquer la fenêtre de niveau supérieur des applications provoque des choses étranges, notamment le menu système incomplet qui s'affiche dans la barre des tâches et des fenêtres de clous de pouce incorrectes dans Vista. Les versions ultérieures de Delphi corrigent cela.

Cependant, toutes les fenêtres ne doivent pas l'avoir comme parent, Windows a simplement tendance à mieux fonctionner s'il l'est. Cependant, tout formulaire créé avec Application.CreateForm l'aura comme parent et il appartiendra également à l'objet Application. Comme ils appartiennent, ils seront libérés une fois l'application libérée. Cela se produit dans les coulisses de Forms.DoneApplication

12
Gerry Coll

En regardant la source dans forms.pas (Delphi 2009), il semble qu'ils créent une fenêtre "principale" dans les applications win32 gui pour autoriser les appels à

  • TApplication.Réduire
  • TApplication.Restore
  • etc

Il semble que les messages transmis au Application.Handle sont transmis comme il convient au MainForm, s'il existe. Cela permettrait à l'application de répondre pour minimiser, etc. si la fenêtre principale n'a pas été créée. En modifiant la source du projet, vous pouvez créer une application delphi sans une fenêtre principale.

Dans ce cas, les méthodes TApplication continueront de fonctionner, même si vous n'avez pas créé de fenêtre principale. Je ne sais pas si je saisis tous les objectifs, mais je n'ai pas le temps de parcourir tout le code TApplication.

Par vos questions:

  • D'où cela vient-il? Il s'agit du handle d'une fenêtre créée dans TApplication.Create

  • De quelle fenêtre s'agit-il? une fausse fenêtre dont chaque application gui delphi a besoin dans le cadre de l'abstraction de TApplication

  • S'agit-il du pseudo Windows du formulaire principal de l'appliation Non

  • Si ce n'est pas le handle du formulaire principal de l'application, alors qu'est-ce que c'est? Voir ci-dessus

  • plus important encore: pourquoi est-il le parent ultime de chaque formulaire? en supposant que vous avez raison que c'est le parent ultime, je suppose que c'est le cas car cela facilite la recherche de tous les formulaires dans votre application (énumérant les enfants de ce formulaire "maître").

  • et le plus important: pourquoi tout se détraque si j'essaie d'avoir un formulaire sans parenté je pense parce que le formulaire "maître" caché reçoit des messages système qu'il devrait passe à ses enfants et/ou à la forme principale, mais ne trouve pas la forme sans parenté.

Quoi qu'il en soit, c'est mon point de vue. Vous pouvez probablement en savoir plus en consultant la déclaration et le code TApplication dans forms.pas. L'essentiel de ce que je vois est que c'est une abstraction pratique.

Meilleures salutations,

Don

8
Don Dickinson