如何从表单外部捕获表单的某些事件?

时间:2012-01-05 14:10:51

标签: delphi delphi-7 windows-messages tform

我正在研究需要监控多种形式的事情。从表单外部,并且不在表单中放置任何代码,我需要以某种方式从这些表单中捕获事件,最有可能以Windows消息的形式。但是你如何捕获与它相关的类外的Windows消息?

我的项目有一个对象,它包装了它正在监视的每个表单,我假设这个处理将进入这个对象。基本上,当我创建一个我想要监视的表单时,我创建了一个相应的对象,该对象又被添加到所有已创建表单的列表中。最重要的是,当该表单关闭时,我必须知道所以我可以从列表中删除此表单的包装器对象。

这些事件包括:

  • 最小化
  • 最大化
  • 恢复
  • 关闭
  • 专注于进/出

我不想要的东西:

  • 此处理的任何表单或表单单元内的任何代码
  • 从任何自定义基本表单继承表单
  • 使用表单的事件,例如OnClose,因为它们将用于其他目的

我想要的是什么:

  • 处理这些事件的Windows消息
  • 有关如何从课堂外获取Windows消息的任何提示
  • 我需要收听哪些Windows消息

使用相同信息但不同方法重写的问题

5 个答案:

答案 0 :(得分:9)

您需要侦听传递给表单的特定Windows消息。最简单的方法是分配表单的WindowProc属性。请务必保留之前的WindowProc值并将其从替换值中调用。

在你的包装器对象中声明一个这样的字段:

FOriginalWindowProc: TWndMethod;

然后在包装器的构造函数中执行以下操作:

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

最后,实现替换窗口过程:

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

答案 1 :(得分:7)

以下是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;

答案 2 :(得分:1)

比尝试在表单之外工作更好的解决方案是使每个表单都从实现该功能的公共基本表单下降。表单事件处理程序正是添加此代码的正确位置,但您将以祖先形式编写它们。任何后代表单仍然可以使用表单事件,只要它们始终在事件处理程序中的某处继承,祖先代码仍将执行。

答案 3 :(得分:0)

使用Windows消息确实可以达到fine granularity(是的,这是您的部分要求!)但在某些仅依赖VCL Event Framework的用户情况下,可以建议类似的解决方案:< / p>

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.

答案 4 :(得分:0)

另一个选项是创建TApplicationEvents并为OnMessage事件分配处理程序。一旦它被触发,使用FindControl函数和Msg.hWnd来检查它是否是tform类型并在没有hookin的情况下做你想做的事