父句柄未到达的消息传递

Message passing with parent handle not reaching

在一个新项目中,我创建了一个带有 2 个面板的 MainForm 和一个带有按钮的 Form。

我在 MainForm 上添加了这段代码:

interface

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Panel2: TPanel;
    procedure FormCreate(Sender: TObject);
  private
     procedure OnMyMessage(var Msg: TMessage); message WM_FILEREADY;
  public
    { Public declarations }
  end;

implementation

uses
  PannelForm;

{$R *.dfm}


procedure TForm1.FormCreate(Sender: TObject);
begin
  with TForm2.Create(self) do
  try
    parent := panel2;
    borderstyle := bsNone;
    InnerHandle := self.Handle;
    Show;

  finally

  end;
end;

procedure TForm1.OnMyMessage(var Msg: TMessage);
begin
  showmessage('got event');
end;

在带有按钮的表单上的这段代码:

type
  TForm2 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    InnerHandle:HWND;
  end;

procedure TForm2.Button1Click(Sender: TObject);
begin
//  PostMessage(Application.Mainform.Handle, WM_FILEREADY, 0, 0); // works
//  PostMessage(Application.Handle, WM_FILEREADY, 0, 0); // not working
//  PostMessage(parent.Handle, WM_FILEREADY, 0, 0); // not working
  PostMessage(InnerHandle, WM_FILEREADY, 0, 0); // works

end;

我的问题是:调用第一版和第四版时,一切正常。

第三个版本中缺少什么不起作用?

为什么 Parent 不包含右句柄?不就是传递一个Parent的(部分)点吗?

您已在 TForm1 中实现了消息处理,但 Form2.Parent.Handle 不是 Form1.Handle,而是您已将 Panel2.Handle 分配给它。

每个窗口控件都有自己的句柄。因此,您的面板具有与表单不同的句柄,它们无法处理在表单 class.

中实现的消息

一切正常,即使它不是您所期望的。

您将 Parent 设置为 Form1.Panel2,而不是 Form1 本身。您的消息处理程序将只接收直接 posted 到 Form1 的消息。您的其他电话正在 post 打到 Form1.Handle,这就是它们起作用的原因。

如果在 Parent 不是 Form1 时要 post 向 Parent.Handle 发送消息,则必须将要分配的面板子类化为 Parent:

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Panel2: TPanel;
    procedure FormCreate(Sender: TObject);
  private
     DefPanelWndProc: TWndMethod;
     procedure PanelWndProc(var Msg: TMessage);
  public
    { Public declarations }
  end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  DefPanelWndProc := Panel2.WindowProc;
  Panel2.WindowProc := PanelWndProc;

  Form2 := TForm2.Create(Self);
  Form2.Parent := Panel2;
  Form2.BorderStyle := bsNone;
  Form2.Show;
end;

procedure TForm1.PanelWndProc(var Msg: TMessage);
begin
  if Msg.Msg = WM_FILEREADY then
    ShowMessage('got event')
  else
    DefPanelWndProc(Msg);
end;

否则,post 将消息改为 Form1

如果你post每次都使用Form1.Handle属性,一切都会好起来的(我不算多线程代码,因为TWinControl.Handle不是线程-安全的)。但是,如果您将 Form1.Handle 的值缓存到一个变量,然后 post 使用该变量,那么如果 Form1.Handle 被重新创建(这可能而且确实会发生),您的代码将停止工作。在这种情况下,您需要检测重新创建并相应地更新变量:

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Panel2: TPanel;
    procedure FormCreate(Sender: TObject);
  protected
    procedure CreateWnd; override;
    procedure DestroyWnd; override;
  private
     procedure OnMyMessage(var Msg: TMessage); message WM_FILEREADY;
  public
    { Public declarations }
  end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Form2 := TForm2.Create(Self);
  Form2.Parent := Panel2;
  Form2.BorderStyle := bsNone;
  Form2.InnerHandle := Self.Handle;
  Form2.Show;
end;

procedure TForm1.CreateWnd;
begin
  inherited;
  if Form2 <> nil then
    Form2.InnerHandle := Self.Handle;
end;

procedure TForm1.DestroyWnd;
begin
  if Form2 <> nil then
    Form2.InnerHandle := 0;
  inherited;
end;

procedure TForm1.OnMyMessage(var Msg: TMessage);
begin
  ShowMessage('got event');
end;

否则,根本不要使用 Form1.Handle。使用永远不会重新创建的不同 window。

您可以使用 AllocateHWnd() 创建专用 window:

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Panel2: TPanel;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
     MsgWnd: HWND;
     procedure MsgWndProc(var Msg: TMessage);
  public
    { Public declarations }
  end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  MsgWnd := AllocateHWnd(MsgWndProc);

  Form2 := TForm2.Create(Self);
  Form2.Parent := Panel2;
  Form2.Borderstyle := bsNone;
  Form2.InnerHandle := MsgWnd;
  Form2.Show;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  if MsgWnd <> 0 then
    DeallocateHWnd(MsgWnd);
end;

procedure TForm1.MsgWndProc(var Msg: TMessage);
begin
  if Msg.Msg = WM_FILEREADY then
    ShowMessage('got event')
  else
    Message.Result := DefWindowProc(MsgWnd, Msg.Msg, Msg.WParam, Msg.LParam);
end;

或者您可以使用 Application.Handle window:

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Panel2: TPanel;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
     function AppWndProc(var Msg: TMessage): Boolean;
  public
    { Public declarations }
  end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Application.HookMainWindow(AppWndProc);

  Form2 := TForm2.Create(Self);
  Form2.Parent := Panel2;
  Form2.Borderstyle := bsNone;
  Form2.InnerHandle := Application.Handle;
  Form2.Show;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin

Application.UnhookMainWindow(AppWndProc); 结束;

function TForm1.AppWndProc(var Msg: TMessage): Boolean:
begin
  if Msg.Msg = WM_FILEREADY then
  begin
    ShowMessage('got event');
    Result := True;
  end else
    Result := False;
end;