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? P>
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. P>
Ces événements incluent: p>
Ce que je ne veux pas: p>
osclose code> car ils seront utilisés à d'autres fins li>
ul>
Qu'est-ce que je veux: p>
- Traitement des messages Windows pour ces événements LI>
- Tout conseils sur la manière de recevoir des messages Windows de l'extérieur de la classe li>
- Quels messages Windows j'ai besoin pour écouter LI>
ul>
question réécrit avec les mêmes informations, mais une approche différente em> p>
5 Réponses :
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. P>
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 i> 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 i>. Le formulaire ancêtre peut avoir du code dans ONCLOSE / Onshow / OnResize, etc. et i> 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é.
Vous devez écouter des messages Windows particuliers étant livrés sur le formulaire. Le moyen le plus simple de le faire est d'attribuer le dans votre objet wrapper déclarer un champ comme celui-ci: P> WindowProc code> propriété de la forme. N'oubliez pas de conserver la valeur précédente de
WindowProc code> et de l'appeler à partir de votre remplacement.
procedure TFormWrapper.NewWindowProc(var Message: TMessage);
begin
//test for and respond to the messages of interest
FOriginalWindowProc(Message);
end;
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 code> 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 code>
.
Je ne savais jamais qu'il y avait un moyen d'attraper tous les messages du même gestionnaire.
Voici un exemple plus complet de la solution fournie par David:
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.
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).
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 p>
Notez toutefois que tapsplication.onMessage code> événement (et
TapLlicationVents CODE> est simplement le multiplicateur de gestionnaire d'événements ici) ne gère pas
wm_cler code> la commande utilisée pour masquer / Gratuit la fenêtre en raison des actions de l'utilisateur. Vous pouvez intercepter
libérer code> du formulaire à l'aide de
TComponent.notification code> mécanisme, mais pour le type
Cahide code> Type formulaires, il ne fonctionnera pas! Le
wm_close code> est généré en interne et est pompé dans le gestionnaire de messagerie de la fenêtre contourner la file d'attente principale code> '(
sendMessage code> vs
postmessage code> ). Pratiquement réalisable semble utiliser
onMessage code> pour intercepter les messages précurseurs
nommément wm_syscommand code> avec
wparam = sc_close code> et
wm_nclbuttonwown code> avec
wparam = htclose code>. 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 code>) Stackoverflow.com/questions/27400973
Oh, un autre "cas de niche" est quand la fermeture est refusé à l'aide de onclose code> ou
onclosequery code> événements, ce sera un cas difficile aussi. Peut-être juste à un
postmessage code> ou
tthread.queue code> un gestionnaire paresseux après-gobelais qui vérifierait le
"du formulaire surveillé". Comme
TOBJECT code> ne serait pas déjà détruit à ce moment-là.
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.