delphiwinapidelphi-10.2-tokyo

SetWindowPlacement makes a modal form to freeze


In the OnCreate event of a modal form, I use SetWindowPlacement() to restore the form's size and position that was previously saved. It works OK in a non-modal form, but it totally freezes on a modal form after a few seconds it appears.

Form1 is calling Form2 as modal.

I cannot use SetWindowPlacement() in the OnShow event because we get the exception:

class EInvalidOperation with message 'Cannot change Visible in OnShow or OnHide'.

The expecting result is to show a modal form on the last location before it was closed. SetWindowPlacement() is the right function to use since it works OK on multiple monitors, even if one of the monitors is unplugged after the location was saved. If we don't consider this possibility, the form will just appear off-screen (hidden).

This is the short piece of code:

procedure TForm1.Button1Click(Sender: TObject);
begin
  Form2 := TForm2.Create(nil);
  Form2.ShowModal;
  FreeAndNil(form2);
end;

procedure TForm2.FormCreate(Sender: TObject);
begin
  //Hard coded paremeters to make it simple
  RestoreFormPosition(self, 100, 200, 301, 463);
end;

procedure TForm2.RestoreFormPosition(WhichForm: TForm; const pTop, pLeft, pHeight, pWidth: Integer);
var
  recWndPlmt : TWindowPlacement;
begin
  WhichForm.Position := poDesigned;

  recWndPlmt.Length:= SizeOf(TWindowPlacement);
  //GetWindowPlacement(WhichForm.Handle, @recWndPlmt);

  recWndPlmt.rcNormalPosition.Top      := pTop;
  recWndPlmt.rcNormalPosition.Left     := pLeft;
  recWndPlmt.rcNormalPosition.Height   := pHeight;
  recWndPlmt.rcNormalPosition.Width    := pWidth;
  recWndPlmt.showCMD:= SW_SHOWNORMAL; //1;

  SetWindowPlacement(WhichForm.Handle, @recWndPlmt);
end;

Solution

  • I don't know the root cause, but I can reproduce the problem in Delphi 12.2. For whatever reason, using SetWindowPlacement() in this manner causes the Form's window to become disabled deep inside the VCL before the window is shown on-screen. In the OnShow event, IsWindowEnabled() returns False, whereas it returns True if SetWindowPlacement() is not used.

    I have opened a bug ticket with Embarcadero:

    RSS-1915: Calling SetWindowPlacement() on a modal form disables the window

    Until Embarcadero fixes this, calling EnableWindow() in the OnShow event solves the problem:

    procedure TForm2.FormShow(Sender: TObject);
    begin
      EnableWindow(Handle, True);
    end;
    

    Using the Form's SetBounds() method instead of SetWindowPlacement() also avoids the problem:

    //SetWindowPlacement(WhichForm.Handle, @recWndPlmt);
    WhichForm.SetBounds(pLeft, pTop, pWidth, pHeight);