Watch, Follow, &
Connect with Us
Public Report
Report From: Delphi-BCB/VCL/MDI Support    [ Add a report in this area ]  
Report #:  66204   Status: Reported
Closing mainform, the childs OnClose isn't called
Project:  Delphi Build #:   11.0.2902.1047
Version:    11.2 Submitted By:   Henrik R. Carlsen
Report Type:  Minor failure / Design problem Date Reported:  8/29/2008 12:43:32 AM
Severity:    Commonly encountered problem Last Updated: 8/29/2008 12:46:53 AM
Platform:    All platforms Internal Tracking #:  
Resolution: None  Resolved in Build: : None
Duplicate of:  None
Voting and Rating
Overall Rating: No Ratings Yet
0.00 out of 5
Total Votes: None
The attached example is an MDI-application with one child. The child has two events, OnClose and OnDestroy. The OnClose sets Action = caFree.

Opening the child and closing it again, calls the OnClose, then the OnDestroy event.

Same scenario, one child-form open. If I try to close the MDIForm hoping it'll rid the child properly, only the OnDestroy is called. There's no telling that the child form is to free itself.

If I debug it seems that the Application.MainForm is nil before the child is gone. It therefore raises this exception.

procedure TCustomForm.CreateWindowHandle(const Params: TCreateParams);
  CreateStruct: TMDICreateStruct;
  NewParams: TCreateParams;
  if (FormStyle = fsMDIChild) and not (csDesigning in ComponentState) then
    if (Application.MainForm = nil) or
      (Application.MainForm.ClientHandle = 0) then
      raise EInvalidOperation.Create(SNoMDIForm); // 'ere!
Steps to Reproduce:
See attachment
Main form OnClose event:

procedure TfrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
  i: integer;
  for i := MDIChildCount - 1 downto 0 do

Lucian Radulescu at 9/4/2008 12:58:14 PM -
It works like this since day Delphi 1 - so, I don't think a bit it is going to change.

I too had this issues in MDI apps and what I learned is that you should not rely at all on what VCL does. What I do is override CloseQuery in my mainforms with code like this:

function TWILLMainForm.CloseQuery: Boolean;
  I: Integer;
  if MDIChildCount <> 0 then
    I := MDIChildCount;
    SendMessage(ActiveMdiChild.Handle, WM_CLOSE, 0, 0);
  until (MDIChildCount = 0) or (I = MDIChildCount);
  Result := (MDIChildCount = 0) and inherited CloseQuery;

I don't like it that I have to call Application.ProcessMessages in that loop, but .... that's the best I could do :-)

BTW, your FormClose does not guarantee all the children are closed. (Overriding) Mainform CloseQuery execution is guaranteed. So your app is still safe up and running if one child does not want to close.


Server Response from: ETNACODE01