Watch, Follow, &
Connect with Us
Public Report
Report From: Delphi-BCB/VCL/Core VCL Classes/TForm    [ Add a report in this area ]  
Report #:  88776   Status: Closed
Access violation on shutdown related to bug in popup children / free notification code
Project:  Delphi Build #:  14.0.3593.25826
Version:    15.0 Submitted By:   David Heffernan
Report Type:  Crash / Data loss / Total failure Date Reported:  10/8/2010 2:49:50 AM
Severity:    Serious / Highly visible problem Last Updated: 4/15/2014 6:50:15 PM
Platform:    All platforms Internal Tracking #:   18005
Resolution: Fixed (Resolution Comments) Resolved in Build: : XE6
Duplicate of:  None
Voting and Rating
Overall Rating: No Ratings Yet
0.00 out of 5
Total Votes: None
Description
The problem occurs when shutting down an application which has a very particular set of PopupParent relationships.  I came across this bug in my (large) app and have spent quite a bit of time reducing the complexity to an understandable level.  It appears to me that the issue arises when you have:

1.  A main form owned by Application.
2.  Another form owned by the main form.
3.  Yet another form owned by the second form, but whose popup parent is the main form.

In this situation, if you allow the app to terminate without explicitly destroying the main form then you sometimes get an access violation.  The 'sometimes' is because the AV is due to freed memory being accessed after it has been freed.  Quite often, due to the way memory managers work, this does not produce an AV.

I've learnt the following about the problem:

The third form (as described above) takes a reference to the first form in TCustomForm.CreateParams.  Specifically, this is done in the line LParent.PopupChildren.Add(Self).  In order for this reference to be remove in case the third form is destroyed it also calls FreeNotification(LParent).  Here LParent is the main form and Self is the third form.

The AV occurs during close down in TWinControl.HandleAllocated.  It reaches here from TCustomForm.WMDestroy where Self is the main form.  The code that calls HandleAllocated is:

  if Assigned(FPopupChildren) then
  begin
    for I := 0 to FPopupChildren.Count - 1 do
      if TCustomForm(FPopupChildren[I]).HandleAllocated then

So what is happening is that the main form is trying to do something with the third form but in fact the third form has already been destroyed.  Consequently an AV can occur.

Now, the main form should no longer hold a reference to the third form because the call to FreeNotification should have ensured that the main form was notified of the third form's demise.  However, right at the start of shutdown the free notifications were all removed which seems like quite a bad idea!  This is triggered by the ShowHint := False line in DoneApplication.  At this point I had reached the limits of my debugging strength and since it's your code not mine I'm going to offer it up to you to investigate.

I've included a very simple app which should produce an AV every time.  You just need to run it up and then close down the main form (captioned 1st level form).

Note that I have made the instance size for the third form very large.  This is to ensure that the memory manager returns the memory to the system as soon as it is freed.  You need to take steps like this to ensure that 'access after free' bugs fire.

You can sidestep the issue by explicitly closing down the main form in the dpr file immediately following Application.Run.
Steps to Reproduce:
None
Workarounds
None
Attachment
PopupParentShutdownBug.zip
Comments

Tomohiro Takahashi at 10/12/2010 12:57:20 AM -
This issue exists in Delphi XE, too.

David Heffernan at 10/13/2010 1:34:13 PM -
Thanks for looking into this.

Dmitry Ivanov at 3/3/2011 8:57:05 AM -
If you use dll and do
Application = Application from exe
then Application.FPopupForms must be iniialized in exe( in main application)
else in dll will call
    SetLength(FPopupForms, I + 1);
and you have AV on close application


how i fexed this problem.

forms.pas
1)add line
const maxFPopupFormsElements=1000; //ida 2011.03.03

2)transfer  property TApplication. FPopupForms into public from private

3) function TApplication.AddPopupForm(APopupForm: TCustomForm): Integer;
var
  I: Integer;
begin
......

>>>>>>>>>>>> add thei lines
  if i>=maxFPopupFormsElements-1
    then begin
           Application.MessageBox(Pchar('maxFPopupFormsElements must be more in forms.pas'),pchar(Application.Title));
           exit;
         end;

>>>>>>>>>>>>>>>>>>

  if I >= Length(FPopupForms) then
  begin
    I := Length(FPopupForms);
    SetLength(FPopupForms, I + 1);
  end;

.....

end;


4) In MainForm.pas initialized array FPopupForms

  ... FormCreate ...

  SetLength(Application.FPopupForms,maxFPopupFormsElements);   for i:=0 to maxFPopupFormsElements-1 do
   begin
     Application.FPopupForms[i].FormID:=-1;
     Application.FPopupForms[i].Form:=nil;
   end;






Server Response from: ETNACODE01