7
votes

Comment attraper certains événements d'une forme de l'extérieur de la forme?

Je travaille sur quelque chose qui nécessitera une surveillance de nombreuses formes. De l'extérieur de la forme, et sans mettre de code à l'intérieur du formulaire, j'ai besoin de capturer des événements de ces formulaires, probablement sous la forme de messages Windows. Mais comment captureriez-vous des messages Windows de l'extérieur de la classe qu'elle est liée?

Mon projet a un objet qui enveloppe chaque formulaire que c'est surveille, et je présume que cette manipulation ira dans cet objet. Essentiellement, lorsque je crée un formulaire, je souhaite surveiller, je crée un objet correspondant qui est à son tour ajouté à une liste de tous les formulaires créés. Plus important encore, quand ce formulaire est fermé, je dois savoir afin que je puisse supprimer l'objet Wrapper de ce formulaire de la liste.

Ces événements incluent:

  • minimiser
  • optimise
  • restaurer
  • Fermer
  • FOCUS IN / OUT

    Ce que je ne veux pas:

    • n'importe quel code à l'intérieur des formulaires ou des unités de formulaire pour cette manutention
    • hériter des formulaires de n'importe quelle forme de base personnalisée
    • en utilisant les événements du formulaire telles que osclose car ils seront utilisés à d'autres fins

      Qu'est-ce que je veux:

      • Traitement des messages Windows pour ces événements
      • Tout conseils sur la manière de recevoir des messages Windows de l'extérieur de la classe
      • Quels messages Windows j'ai besoin pour écouter

        question réécrit avec les mêmes informations, mais une approche différente


3 commentaires

Je ne suis pas si sûr, mais je pense que vous pouvez également envisager d'envisager une injection de code, comme cela a été fait par un cadre AOP.


Vous savez que vous pouvez remplacer les événements de formulaire avec votre propre gestionnaire, mais garder autour de la valeur ancienne, puis appelez l'ancien gestionnaire, de votre gestionnaire de remplacement, non? C'est plus simple que la vraie "injection de code" ou véritable "accrochage". Cela ressemble beaucoup à la manière dont les "manipulateurs d'interruption" travaillent dans la plupart des systèmes d'exploitation. Nous appelons cela "remplacement de vecteur".


@Warrenp je le sais, et je le ferais probablement si David n'avait pas mentionné une méthode de nettoyage. Mais cette stratégie (du moins à mon avis) est probablement efficace de 90 à 95% (je peux prévoir certains problèmes qui gâcheraient cette situation). La solution de David est 100% efficace.


5 Réponses :


1
votes

Une meilleure solution que d'essayer de travailler en dehors du formulaire serait de rendre chaque formulaire descendre d'une forme de base commune qui implémente la fonctionnalité. Les gestionnaires d'événements de formulaire sont exactement le bon endroit pour ajouter ce code mais vous l'écririez tout dans le formulaire d'ancêtre. Toute forme descendante pourrait toujours utiliser les événements de formulaire et tant qu'ils appellent toujours hérité quelque part dans le gestionnaire d'événements, le code des ancêtres exécuterait toujours.


8 commentaires

Je suis d'accord avec cela, mais parfois vous y compris le 3ème code de parti dans votre projet, qui peut rendre cela difficile à atteindre


C'était mon plan original, mais la raison principale que cela ne fonctionnera pas est parce que - comme mentionné dans ma question - je ne peux mettre de code à l'intérieur d'aucun formulaire pour cela. Faire une forme de base n'est pas différent de la mise à l'intérieur du formulaire.


@Jerry - comprise mais vous avez également indiqué que vous alliez finalement ajouter de code à des gestionnaires d'événements de formulaire individuels qui mettent également du code à l'intérieur des formulaires.


C'est une histoire différente. Ce code n'a rien à voir avec ma question, et évidemment, chaque formulaire aura une sorte de code. Mon point était que depuis que j'utilise les gestionnaires d'événements du formulaire pour Autres fins J'ai besoin de trouver autre chose à attraper ces événements.


De plus, vous pouvez mettre un gestionnaire d'événements sur les événements du formulaire, que vous mettiez le code à l'intérieur ou à l'extérieur du formulaire.


Ce que je dis, c'est que vous pouvez faire les deux . Le formulaire ancêtre peut avoir du code dans ONCLOSE / Onshow / OnResize, etc. et Les formulaires descendants peuvent avoir leur propre code pour ces manutentionnaires.


Je comprends, mais comme je le mentionne dans ma question, et comme David l'explique, parfois, vous avez parfois des formes que vous ne pouvez pas changer et de capturer ces choses de l'extérieur. Eh bien, c'est exactement mon cas, je n'ai absolument aucune possibilité de modifier ces formulaires, et surtout de ne pas le faire hériter d'un autre formulaire de base.


En fait, toute la raison pour laquelle je pose cette question est parce que je ne peux pas gérer cela de l'intérieur des formes. Sinon, je n'aurais jamais jamais demandé.



9
votes

5 commentaires

Cela semble prometteur, mais ne peut pas dire à coup sûr jusqu'à ce que je rentre à la maison et que je lui donne une course.


J'étais sur le point de vous demander comment gérer les messages dans NewwindowProc mais Mike m'a battue - je vais accepter sa réponse maintenant :( toujours +1 pour une réponse originale


Oh, je pensais que c'était le bit facile, alors je faisais juste le fort. Je n'ai abordé que l'interception du message depuis la mise au point de la question et des commentaires.


Fwiw Vous n'avez pas posé de questions sur la souris Enter / partir. Vous avez posé des questions sur la focus et pour que vous puissiez écouter wm_activate .


Je ne savais jamais qu'il y avait un moyen d'attraper tous les messages du même gestionnaire.



7
votes

Voici un exemple plus complet de la solution fournie par David: xxx


0 commentaires

0
votes

Utilisation de messages Windows peut réellement atteindre un granularité fine code> (oui, sa partie de vos exigences!) Mais dans certains cas d'utilisateur où s'appuyant uniquement sur le cadre d'événement VCL CODE> suffit , une solution similaire peut être suggérée:

unit Host;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  THostForm = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    FFormResize: TNotifyEvent;
    FFormActivate: TNotifyEvent;
    FFormDeactivate: TNotifyEvent;
    FFormDestroy: TNotifyEvent;

    procedure _FormResize(Sender: TObject);
    procedure _FormActivate(Sender: TObject);
    procedure _FormDeactivate(Sender: TObject);

    procedure InternalEventHandlerInit(const AForm:TForm);
  public
    procedure Log(const Msg:string);
    procedure Logln(const Msg:string);
  end;

var
  HostForm: THostForm;

implementation

{$R *.dfm}

procedure THostForm.Button1Click(Sender: TObject);
var
  frm: TForm;
begin
  frm := TForm.Create(nil);
  frm.Name := 'EmbeddedForm';
  frm.Caption := 'Embedded Form';
  //
  InternalEventHandlerInit(frm);
  //
  Logln('<'+frm.Caption+'> created.');
  //
  frm.Show;
end;


procedure THostForm.InternalEventHandlerInit(const AForm: TForm);
begin
  FFormResize := AForm.OnResize;
  AForm.OnResize := _FormResize;
  //
  FFormActivate :=  AForm.OnActivate;
  AForm.OnActivate := _FormActivate;
  //
  FFormDeactivate :=  AForm.OnDeactivate;
  AForm.OnDeactivate := _FormDeactivate;
end;

procedure THostForm.Log(const Msg: string);
begin
  Memo1.Lines.Add(Msg);
end;

procedure THostForm.Logln(const Msg: string);
begin
  Memo1.Lines.Add(Msg);
  Memo1.Lines.Add('');
end;

procedure THostForm._FormActivate(Sender: TObject);
begin
  Log('Before OnActivate <'+(Sender as TCustomForm).Caption+'>');
  //
  if Assigned(FFormActivate) then
    FFormActivate(Sender) // <<<
  else
    Log('No OnActivate Event Handler attached in <'+(Sender as TCustomForm).Caption+'>');
  //
  Logln('After OnActivate <'+(Sender as TCustomForm).Caption+'>');
end;

procedure THostForm._FormDeactivate(Sender: TObject);
begin
  Log('Before OnDeactivate <'+(Sender as TCustomForm).Caption+'>');
  //
  if Assigned(FFormDeactivate) then
    FFormDeactivate(Sender)
  else
    Log('No OnDeActivate Event Handler attached in <'+(Sender as TCustomForm).Caption+'>');
  //
  Logln('After OnDeactivate <'+(Sender as TCustomForm).Caption+'>');
end;

procedure THostForm._FormResize(Sender: TObject);
begin
  Log('Before OnResize <'+(Sender as TCustomForm).Caption+'>');
  //
  if Assigned(FFormResize) then
    FFormResize(Sender)
  else
    Log('No OnResize Event Handler attached in <'+(Sender as TCustomForm).Caption+'>');
  //
  Logln('After OnResize <'+(Sender as TCustomForm).Caption+'>');
end;

end.


2 commentaires

C'est bien jusqu'à ce que le formulaire de victime décide de modifier ses propres manutes d'événement, puis sa partie.


@David Heffernan: Vous avez raison. Maintenant, je vois qu'ils ne sont pas protégés du tout. J'ai bien peur que le seul moyen d'aller de cette façon dans ce cas est de faire une certaine injection de code sur la forme «victime» (spéculation).



1
votes

Une autre option est la création de tassesplicationsVents et attribuez un gestionnaire à l'événement OnMessage. Une fois si elle est tirée, utilisez la fonction FindControl et msg.hwnd pour vérifier s'il s'agit du type Tform et faites ce que vous voulez sans accrochaine


3 commentaires

Notez toutefois que tapsplication.onMessage événement (et TapLlicationVents est simplement le multiplicateur de gestionnaire d'événements ici) ne gère pas wm_cler la commande utilisée pour masquer / Gratuit la fenêtre en raison des actions de l'utilisateur. Vous pouvez intercepter libérer du formulaire à l'aide de TComponent.notification mécanisme, mais pour le type Cahide Type formulaires, il ne fonctionnera pas! Le wm_close est généré en interne et est pompé dans le gestionnaire de messagerie de la fenêtre contourner la file d'attente principale '( sendMessage vs postmessage ). Pratiquement réalisable semble utiliser onMessage pour intercepter les messages précurseurs


nommément wm_syscommand avec wparam = sc_close et wm_nclbuttonwown avec wparam = htclose . Mais si les futures versions Windows ajouteraient plus de moyens de fermer des formulaires, cette liste devrait être étendue !!! En outre, il faut idéalement rendre compte d'une manière d'une manière ou d'une autre pour les modifications de la poignée occasionnelle surveillé (AKA recuewnd ) Stackoverflow.com/questions/27400973


Oh, un autre "cas de niche" est quand la fermeture est refusé à l'aide de onclose ou onclosequery événements, ce sera un cas difficile aussi. Peut-être juste à un postmessage ou tthread.queue un gestionnaire paresseux après-gobelais qui vérifierait le "du formulaire surveillé". Comme TOBJECT ne serait pas déjà détruit à ce moment-là.