none
none

Reputation: 4827

Message passing with parent handle not reaching

In a new project, I created a MainForm with 2 panels, and a Form with a button.

I added this code on the 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;

And this code on the Form with a button:

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;

My question is: when calling the first and forth version, everything is fine.

What is missing in the third version that is not working?

Why doesn't Parent contain the right handle? Isn't it the (part of) point of passing a Parent?

Upvotes: 0

Views: 761

Answers (2)

Remy Lebeau
Remy Lebeau

Reputation: 595672

You set the Parent to be Form1.Panel2, not Form1 itself. Your message handler will only receive messages that are posted to Form1 directly. Your other calls are posting to Form1.Handle, which is why they work.

If you want to post the message to Parent.Handle when Parent is not Form1, you will have to subclass the Panel you are assigning as 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;

Otherwise, post the message to Form1 instead.

If you post using the Form1.Handle property each time, everything will be fine (I am not counting multi-threaded code, as TWinControl.Handle is not thread-safe). However, if you cache the value of Form1.Handle to a variable and then post using that variable, your code will stop working if Form1.Handle is recreated (which can and does happen). In that case, you need to detect the recreation and update the variable accordingly:

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;

Otherwise, don't use Form1.Handle at all. Use a different window that will never be recreated.

You can use AllocateHWnd() to create a dedicated 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;

Or you can use the 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); end;

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;

Upvotes: 1

Dalija Prasnikar
Dalija Prasnikar

Reputation: 28517

You have implemented message handling in TForm1, but Form2.Parent.Handle is not Form1.Handle instead you have assigned Panel2.Handle to it.

Each windowed control has its own handle. So your panels have different handles than your form and they cannot process messages that are implemented in Form class.

Everything works as it should, even though it is not what you expect.

Upvotes: 2

Related Questions