2012-01-05 18 views
7

Sto lavorando a qualcosa che richiederà il monitoraggio di molte forme. Dall'esterno del modulo e senza inserire alcun codice all'interno del modulo, ho bisogno di catturare in qualche modo eventi da queste forme, molto probabilmente sotto forma di messaggi di Windows. Ma come cattureresti i messaggi di Windows dall'esterno della classe a cui è correlato?Come posso rilevare determinati eventi di un modulo dall'esterno del modulo?

Il mio progetto ha un oggetto che avvolge ogni forma che sta monitorando, e presumo che questa gestione andrà in questo oggetto. Essenzialmente, quando creo un modulo che voglio monitorare, creo un oggetto corrispondente che a sua volta viene aggiunto a un elenco di tutte le forme create. Soprattutto, quando quel modulo è chiuso, devo sapere che posso rimuovere l'oggetto wrapper di questo modulo dall'elenco.

Tali eventi comprendono:

  • Minimizzare
  • Massimizzare
  • Restore
  • Chiudi
  • Focus in/out

quello che non voglio:

  • Qualsiasi codice all'interno di qualsiasi forma o unità di forma per questa gestione
  • Ereditando le forme da ogni forma di base personalizzato
  • Uso degli eventi del form, come OnClose perché saranno utilizzati per altri scopi

Cosa io voglio:

  • Gestione dei messaggi di Windows per questi eventi
  • Eventuali suggerimenti su come ge messaggi t finestre dall'esterno della classe
  • Quali messaggi finestre ho bisogno di ascoltare per

domanda ri-scritto con le stesse informazioni, ma diverso approccio

+0

Non ne sono così sicuro ma penso che potresti anche considerare l'iniezione di codice come era fatto da qualche framework AOP. – menjaraz

+0

Si sa che è possibile sostituire gli eventi del modulo con il proprio gestore ma mantenere il valore precedente e quindi richiamare il gestore vecchio dal gestore di sostituzione, giusto? È più semplice della vera "iniezione di codice" o vero "aggancio". Questo è molto simile a come gli "interrupt handlers" funzionano nella maggior parte dei sistemi operativi. Lo chiamiamo "sostituzione vettoriale". –

+0

@WarrenP Lo so, e probabilmente lo farei se David non avesse menzionato un metodo più pulito. Ma questa strategia (almeno secondo me) è probabilmente efficace al 90-95% (posso prevedere alcuni problemi che potrebbero rovinare questa situazione). La soluzione di David è efficace al 100%. –

risposta

6

Ecco un esempio più completo della soluzione fornita da David:

private 
    { Private declarations } 
    SaveProc : TWndMethod; 
    procedure CommonWindowProc(var Message: TMessage); 

... 

procedure TForm1.Button1Click(Sender: TObject); 
var 
    f : tForm2; 
begin 
    f := tForm2.Create(nil); 
    SaveProc := f.WindowProc; 
    f.WindowProc := CommonWindowProc; 
    f.Show; 
end; 

procedure TForm1.CommonWindowProc(var Message: TMessage); 
begin 
    case Message.Msg of 
    WM_SIZE : Memo1.Lines.Add('Resizing'); 
    WM_CLOSE : Memo1.Lines.Add('Closing'); 
    CM_MOUSEENTER : Memo1.Lines.Add('Mouse enter form'); 
    CM_MOUSELEAVE : Memo1.Lines.Add('Mouse leaving form'); 
    // all other messages will be available as needed 
    end; 
    SaveProc(Message); // Call the original handler for the other form 
end; 
1

Una soluzione migliore che cercare di lavorare al di fuori della forma sarebbe quello di far discendere ogni forma da un modulo base comune che implementa la funzionalità. I gestori di eventi del modulo sono esattamente il posto giusto per aggiungere questo codice, ma lo si scriverà tutto nel modulo dell'antenato. Qualsiasi forma discendente potrebbe ancora utilizzare gli eventi del modulo e fintanto che chiamano sempre ereditati da qualche parte nel gestore eventi, il codice dell'antenato verrebbe comunque eseguito.

+1

Sono d'accordo con questo, ma a volte includi codice di terze parti nel tuo progetto che può renderlo difficile da raggiungere –

+0

Questo era il mio piano originale, ma la ragione principale per cui questo non funziona è perché - come accennato nella mia domanda - Non posso inserire qualsiasi codice all'interno di qualsiasi modulo per questo. Creare un modulo base non è diverso dal mettere il codice all'interno del modulo. –

+0

@Jerry - ho capito, ma hai anche indicato che alla fine avresti aggiunto il codice ai gestori di eventi di singoli moduli che stanno anche inserendo il codice all'interno dei moduli. –

8

È necessario ascoltare determinati messaggi di Windows inviati al modulo. Il modo più semplice per farlo è assegnare la proprietà WindowProc del modulo. Ricordati di mantenere il valore precedente di WindowProc e chiamalo dalla tua sostituzione.

Nel vostro oggetto wrapper dichiarare un campo come questo:

FOriginalWindowProc: TWndMethod; 

Poi nel costruttore di involucro fare questo:

FOriginalWindowProc := Form.WindowProc; 
Form.WindowProc := NewWindowProc; 

Infine, applicare la procedura finestra di sostituzione:

procedure TFormWrapper.NewWindowProc(var Message: TMessage); 
begin 
    //test for and respond to the messages of interest 
    FOriginalWindowProc(Message); 
end; 
+0

Sembra promettente, ma non posso ancora dirlo con certezza fino a quando non torno a casa e faccio una corsa. –

+0

Stavo per chiederti come gestire i messaggi in 'NewWindowProc', ma Mike mi ha battuto per farlo - Dovrò accettare la sua risposta ora :(ancora +1 per la risposta originale –

+0

Oh pensavo che fosse un po 'facile Ho appena fatto il duro lavoro, ho solo affrontato l'intercettazione dei messaggi poiché l'intera focalizzazione della domanda e dei commenti era quella parte. –

0

che utilizzano i messaggi di Windows può davvero raggiungere un fine granularity (Sì, una parte delle vostre esigenze la sua!), Ma in alcuni casi di utenti in cui basandosi solo sulle VCL Event Framework suffissi, una soluzione simile può essere suggerito:

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. 
+3

Questo va bene fino a quando il modulo della vittima decide di modificare i propri gestori di eventi e quindi il suo gioco finito. –

+0

@David Heffernan: hai ragione. Ora vedo che non sono affatto protetti. Temo che l'unico modo per fare in questo modo in questo caso sia fare un'iniezione di codice nel modulo "vittima" (speculazione). – menjaraz

0

Un'altra opzione è creare TApplicationEvents e assegnare un gestore all'evento OnMessage. Una volta attivato, usa la funzione FindControl e Msg.hWnd per verificare se è il tipo di Tform e fai quello che vuoi senza hookin