2012-01-05 25 views
7

Estoy trabajando en algo que requerirá el monitoreo de muchas formas. Desde fuera del formulario, y sin poner ningún código dentro del formulario, necesito capturar de alguna manera los eventos de estos formularios, muy probablemente en forma de mensajes de Windows. Pero, ¿cómo capturaría los mensajes de Windows desde fuera de la clase con la que está relacionado?¿Cómo capturo ciertos eventos de un formulario desde fuera del formulario?

Mi proyecto tiene un objeto que envuelve cada formulario que está supervisando, y supongo que este manejo irá en este objeto. Esencialmente, cuando creo un formulario que quiero monitorear, creo un objeto correspondiente que a su vez se agrega a una lista de todos los formularios creados. Lo que es más importante, cuando ese formulario está cerrado, tengo que saberlo para poder eliminar el objeto envoltorio de este formulario de la lista.

Estos eventos incluyen:

  • Minimizar Maximizar
  • Restaurar
  • Cerrar
  • foco de entrada/salida

Lo que no quiero:

  • Cualquier código dentro de cualquier forma o unidades de la forma de este manejo
  • La herencia de las formas de cualquier forma base personalizado
  • Usando eventos del formulario como OnClose porque van a ser utilizados para otros fines

Lo yo QUIERO:

  • Manejo de mensajes de ventanas para estos eventos
  • Algún consejo sobre cómo ge Mensajes t ventanas desde fuera de la clase
  • Qué mensajes ventanas que necesito para escuchar

Pregunta re-escrito con misma información pero enfoque diferente

+0

No estoy tan seguro pero creo que también puede considerar la inyección de código como lo fue hecho por algún marco AOP. – menjaraz

+0

Sabe que puede reemplazar los eventos de formulario con su propio controlador pero mantener el valor anterior y luego invocar el controlador anterior desde su controlador de reemplazo, ¿verdad? Eso es más simple que la verdadera "inyección de código" o verdadero "enganche". Esto es muy parecido a cómo funcionan los "manejadores de interrupción" en la mayoría de los sistemas operativos. Lo llamamos "reemplazo de vectores". –

+0

@WarrenP Lo sé, y probablemente lo haría si David no hubiera mencionado un método más limpio. Pero esta estrategia (al menos en mi opinión) es probablemente 90-95% efectiva (puedo prever algunos problemas que podrían estropear esta situación). La solución de David es 100% efectiva. –

Respuesta

6

Aquí hay un ejemplo más completo de la solución que David proporcionó:

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 solución mejor que tratar de trabajar fuera del formulario sería hacer que cada forma descienda de una base común que implemente la funcionalidad. Los manejadores de eventos de formulario son exactamente el lugar correcto para agregar este código pero lo escribirías todo en la forma ancestral. Cualquier formulario de descendiente aún podría usar los eventos de formulario y siempre que invoquen heredado en alguna parte del controlador de eventos, el código ancestro se ejecutará.

+1

Estoy de acuerdo con esto, pero a veces incluye código de terceros en su proyecto que puede hacer que sea difícil de lograr –

+0

Este fue mi plan original, pero la razón clave por la que esto no funcionará es porque, como mencioné en mi pregunta, no puedo poner cualquier código dentro de cualquier forma para esto. Hacer una forma base no es diferente a poner el código dentro del formulario. –

+0

@Jerry - entendido, pero también indicó que eventualmente agregaría código a los manejadores de eventos de formulario individuales, que también está poniendo código dentro de los formularios. –

8

Debe escuchar los mensajes de Windows que se entregan al formulario. La forma más sencilla de hacerlo es asignar la propiedad WindowProc del formulario. Recuerde mantener el valor anterior de WindowProc y llamarlo desde su reemplazo.

En su objeto envoltorio declarar un campo como este:

FOriginalWindowProc: TWndMethod; 

A continuación, en el constructor de la envoltura hacer esto:

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

Por último, poner en práctica el procedimiento de sustitución de ventanas:

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

Parece prometedor, pero no puedo decirlo con seguridad hasta que llegue a casa y lo pruebe. –

+0

Estaba a punto de preguntarle cómo manejar los mensajes en 'NewWindowProc', pero Mike me ganó. Tendré que aceptar su respuesta ahora :(todavía +1 para la respuesta original –

+0

Oh, pensé que eso era lo fácil. Acabo de hacer lo difícil. Solo abordé la interceptación de mensajes ya que todo el enfoque de la pregunta y los comentarios era esa parte. –

0

Uso de mensajes de Windows realmente puede alcanzar una fine granularity (Sí, es parte de sus necesidades!) Pero en algunos casos los usuarios, cuando dependen sólo de los VCL Event Framework basta, una solución similar se puede sugerir:

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

Esto está bien hasta que la forma de víctima decide modificar sus propios controladores de eventos y luego se termina el juego. –

+0

@David Heffernan: Tienes razón. Ahora veo que no están protegidos en absoluto. Me temo que la única forma de avanzar de esta manera en este caso es hacer una inyección de código a la forma de "víctima" (especulación). – menjaraz

0

Otra opción es crear TApplicationEvents y asignar un controlador al evento OnMessage. Una vez que se haya disparado, use la función FindControl y Msg.hWnd para verificar si es el tipo de tform y haga lo que quiera sin enganchar

Cuestiones relacionadas