Recent

Author Topic: [SOLVED] Access violation when ending program / Forms with Parent 'Application'  (Read 7533 times)

Hartmut

  • Hero Member
  • *****
  • Posts: 1000
You have a control linked to another, like Actions for example that are not getting notified that the control no longer exists.
My progrom uses no 'Actions'. I dont know what you mean with 'You have a control linked to another'...

One thought, which I don't know if it will help but, maybe running your program under that old 32bit Win 7 might shed some light as to what went wrong in the Linux environment.
I tried my program several times on Win7 32-bit and played with a lot of modal and non modal dialogs, but no AV occured. I have a handful of Users, which use this program on Win10 and Win11, but noone reported an AV. I'm the only one who uses this program on Linux.

You should manage your own exitproc correctly, not by setting it directly, but by using AddExitProc instead.
That does not break the exitproc chain and prevents that an exitproc can not be executed.
This can very well be the cause of your problem.
As I wrote:
I remember var system.exitProc from good old Turbo Pascal times. I found some usages of this var in some of my common units, but I checked twice, that they all are never used in this project.



As I asked before:
I hope for more benefits by adding some more debug outputs at meaningful source positions, so that I get more detailed informations, if the AV occors again some day.
E.g. by improving procedure show_Application_Components() in reply #5, e.g. by trying to detect, which of those following 'Components[].Free' in procedure TApplication.DoBeforeFinalization() - also shown in reply #5 - will fail.
Are there any ideas for?

Hartmut

  • Hero Member
  • *****
  • Posts: 1000
I had a closer look at the "Stack trace:" table from reply #5:
Code: Text  [Select][+][-]
  1. [FORMS.PP] ExceptionOccurred
  2.   Sender=EAccessViolation
  3.   Exception=Access violation
  4.   Stack trace:
  5.   $00000000005D1B42 = CONTROLS$_$TCONTROL_$__$$_DESTROY + 434
  6.   $00000000005BEF09 = CONTROLS$_$TWINCONTROL_$__$$_DESTROY + 457
  7.   $000000000062C62E = STDCTRLS$_$TCUSTOMEDIT_$__$$_DESTROY + 78
  8.   $000000000062B12E = STDCTRLS$_$TCUSTOMMEMO_$__$$_DESTROY + 110
  9.   $000000000051ABB6 = CLASSES$_$TCOMPONENT_$__$$_DESTROYCOMPONENTS + 54
  10.   $00000000005D1C6E = CONTROLS$_$TCONTROL_$__$$_DESTROY + 734
  11.   $00000000005BEF09 = CONTROLS$_$TWINCONTROL_$__$$_DESTROY + 457
  12.   $00000000005D476E = CONTROLS$_$TCUSTOMCONTROL_$__$$_DESTROY + 78
  13.   $0000000000448ADE = FORMS$_$TSCROLLINGWINCONTROL_$__$$_DESTROY + 94
  14.   $000000000044A4F6 = FORMS$_$TCUSTOMFORM_$__$$_DESTROY + 278
  15.   $0000000000430ACB = SYSTEM$_$TOBJECT_$__$$_FREE + 27
  16.   $0000000000445886 = FORMS_$$_BEFOREFINALIZATION + 22
  17.   $00000000004355A9 = SYSTEM_$$_INTERNALEXIT + 89

We know, that the AV occured (during program termination), while procedure Forms.BeforeFinalization() was excecuted, which only calls Application.DoBeforeFinalization():
Code: Pascal  [Select][+][-]
  1. procedure TApplication.DoBeforeFinalization;
  2. var
  3.   i: Integer;
  4. begin
  5.   if Self=nil then exit;
  6.   for i := ComponentCount - 1 downto 0 do
  7.   begin
  8.     // DebugLn('TApplication.DoBeforeFinalization ',DbgSName(Components[i]));
  9.     if i < ComponentCount then
  10.       Components[i].Free;
  11.   end;
  12. end;
This procedure calls Free() for all existing Application.Components[].

Question1:
What does Application.Components[] contain?
My suspicion is, that this array contains all GUI-Controls (?) which have been created with its Parent=Application.
Can somebody confirm, whether this is correct? Or give a better answer?

I tried to guess from the "Stack trace" table, which type of GUI-Control was just trying to Free(), when the AV occured:
Code: Text  [Select][+][-]
  1.   Stack trace:
  2.   $00000000005D1B42 = CONTROLS$_$TCONTROL_$__$$_DESTROY + 434
  3.   $00000000005BEF09 = CONTROLS$_$TWINCONTROL_$__$$_DESTROY + 457
  4.   $000000000062C62E = STDCTRLS$_$TCUSTOMEDIT_$__$$_DESTROY + 78
  5.   $000000000062B12E = STDCTRLS$_$TCUSTOMMEMO_$__$$_DESTROY + 110
  6.   $000000000051ABB6 = CLASSES$_$TCOMPONENT_$__$$_DESTROYCOMPONENTS + 54
  7.   $00000000005D1C6E = CONTROLS$_$TCONTROL_$__$$_DESTROY + 734
  8.   $00000000005BEF09 = CONTROLS$_$TWINCONTROL_$__$$_DESTROY + 457
  9.   $00000000005D476E = CONTROLS$_$TCUSTOMCONTROL_$__$$_DESTROY + 78
  10.   $0000000000448ADE = FORMS$_$TSCROLLINGWINCONTROL_$__$$_DESTROY + 94
  11.   $000000000044A4F6 = FORMS$_$TCUSTOMFORM_$__$$_DESTROY + 278
  12.   $0000000000430ACB = SYSTEM$_$TOBJECT_$__$$_FREE + 27
  13.   $0000000000445886 = FORMS_$$_BEFOREFINALIZATION + 22
  14.   $00000000004355A9 = SYSTEM_$$_INTERNALEXIT + 89
The 6 yellow lines look, as if a TForm (or its descendant) was Free'd: called are 6 Destroy's (from bottom to top: TCustomForm, TScrollingWinControl, TCustomControl, TWinControl, TControl, TComponent) which match to the Inheritance table in https://lazarus-ccr.sourceforge.io/docs/lcl/forms/tform.html

Code: Text  [Select][+][-]
  1.   Stack trace:
  2.   $00000000005D1B42 = CONTROLS$_$TCONTROL_$__$$_DESTROY + 434
  3.   $00000000005BEF09 = CONTROLS$_$TWINCONTROL_$__$$_DESTROY + 457
  4.   $000000000062C62E = STDCTRLS$_$TCUSTOMEDIT_$__$$_DESTROY + 78
  5.   $000000000062B12E = STDCTRLS$_$TCUSTOMMEMO_$__$$_DESTROY + 110
  6.   $000000000051ABB6 = CLASSES$_$TCOMPONENT_$__$$_DESTROYCOMPONENTS + 54
  7.   $00000000005D1C6E = CONTROLS$_$TCONTROL_$__$$_DESTROY + 734
  8.   $00000000005BEF09 = CONTROLS$_$TWINCONTROL_$__$$_DESTROY + 457
  9.   $00000000005D476E = CONTROLS$_$TCUSTOMCONTROL_$__$$_DESTROY + 78
  10.   $0000000000448ADE = FORMS$_$TSCROLLINGWINCONTROL_$__$$_DESTROY + 94
  11.   $000000000044A4F6 = FORMS$_$TCUSTOMFORM_$__$$_DESTROY + 278
  12.   $0000000000430ACB = SYSTEM$_$TOBJECT_$__$$_FREE + 27
  13.   $0000000000445886 = FORMS_$$_BEFOREFINALIZATION + 22
  14.   $00000000004355A9 = SYSTEM_$$_INTERNALEXIT + 89
And the 4 yellow lines above look, as if a TMemo (or its descendant) was trying to Free: called are 4 Destroy's (from bottom to top: TCustomMemo, TCustomEdit, TWinControl, TControl) which match to the Inheritance table in https://lazarus-ccr.sourceforge.io/docs/lcl/stdctrls/tmemo.html

Question 2:
Is this correct? Can we rely, that the AV occured during the attempt, to Free a TForm (or its descendant), which contains (at least) 1 TMemo (or its descendant)? This would reduce the number of possible Forms in my program significantly...

Question 3:
If question 2 is true: then we see, that a TMemo (or its descendant) was the very 1st GUI-Control of a Form, which was tried to Free (after freeing the Form). Can we deduce from that, that a TMemo (or its descendant) must be the very first (or very last?) declared GUI-Control in that Form? This would reduce the number of possible Forms in my program more significantly...
If yes: very first or very last?

Khrys

  • Sr. Member
  • ****
  • Posts: 342
The stack trace does indeed point towards a  TMemo  contained at the top level of a  TForm  whose parent is the  Application  singlet.
It doesn't tell you anything about the declaration order of the  TMemo,  though.  TComponent.DestroyComponents()  contains a loop, and the stack trace doesn't tell you which iteration it was in when the segfault occurred.

If I were you, I'd check all forms with a top-level  TMemo  which also serves as the anchor for some other component(s), due to the offending code sitting behind  if FAnchoredControls <> Nil.

And if that still isn't enough to identify the issue, I'd definitely recommend giving  valgrind  a try (or better yet, ASAN via the LLVM backend, though building an LLVM-enabled compiler and using it in Lazarus can be quite a hassle).

Hartmut

  • Hero Member
  • *****
  • Posts: 1000
Thank you very much Khrys for your new reply.

The stack trace does indeed point towards a  TMemo  contained at the top level of a  TForm  whose parent is the  Application  singlet.
I'm not sure what you mean exactly with "top level" here. Is this correct: you don't mean the declaration order within the Form, but you mean, that a TMemo is declared directly inside a Form and not indirectly e.g. via onother class, which contains a TMemo?

Quote
It doesn't tell you anything about the declaration order of the  TMemo,  though.  TComponent.DestroyComponents()  contains a loop, and the stack trace doesn't tell you which iteration it was in when the segfault occurred.
Does this mean:
 - you have a Form which has its Parent=Application and this Form has plenty of GUI-controls with 1 TMemo declared "in the middle"
 - then at program termination procedure TApplication.DoBeforeFinalization() calls Application.Components[].Free() for that Form and during the Free() of this TMemo an AV occures in destructor TControl.Destroy() like an AV occured in my case
 - then the Stack Trace would be "the same" as I had, which means, this TMemo can be declared within the Form at any position order?

Quote
If I were you, I'd check all forms with a top-level  TMemo  which also serves as the anchor for some other component(s), due to the offending code sitting behind  if FAnchoredControls <> Nil.
I'm not so familiar with anchors, but I remember very dark having used something like that some times. I will research, if this programm is affected (directly or via some common unit), but give me some time for that.
Can you tell, whether the problematic anchoring must have been initiated from a TMemo to onother Control? Or is "vice versa" also possible?

Quote
And if that still isn't enough to identify the issue, I'd definitely recommend giving  valgrind  a try
Are you sure, that valgrind surely will identify the problem, even if that AV does not occur?

Khrys

  • Sr. Member
  • ****
  • Posts: 342
The stack trace does indeed point towards a  TMemo  contained at the top level of a  TForm  whose parent is the  Application  singlet.
I'm not sure what you mean exactly with "top level" here. Is this correct: you don't mean the declaration order within the Form, but you mean, that a TMemo is declared directly inside a Form and not indirectly e.g. via onother class, which contains a TMemo?

Yes, "top-level" means that it isn't nested further (e.g. inside a  TPanel) but a direct member of the form.

Quote
It doesn't tell you anything about the declaration order of the  TMemo,  though.  TComponent.DestroyComponents()  contains a loop, and the stack trace doesn't tell you which iteration it was in when the segfault occurred.
Does this mean:
 - you have a Form which has its Parent=Application and this Form has plenty of GUI-controls with 1 TMemo declared "in the middle"
 - then at program termination procedure TApplication.DoBeforeFinalization() calls Application.Components[].Free() for that Form and during the Free() of this TMemo an AV occures in destructor TControl.Destroy() like an AV occured in my case
 - then the Stack Trace would be "the same" as I had, which means, this TMemo can be declared within the Form at any position order?

Yes. The  TMemo  whose destructor causes the segfault may be located at any index within the form's  Components, and the form may in turn be located at any index within  Application's  Components.

Quote
If I were you, I'd check all forms with a top-level  TMemo  which also serves as the anchor for some other component(s), due to the offending code sitting behind  if FAnchoredControls <> Nil.
I'm not so familiar with anchors, but I remember very dark having used something like that some times. I will research, if this programm is affected (directly or via some common unit), but give me some time for that.
Can you tell, whether the problematic anchoring must have been initiated from a TMemo to onother Control? Or is "vice versa" also possible?

Self.FAnchoredControls  stores a list of components whose anchors depend in some way on  Self,  so it should be anchors from other controls to the  TMemo  and not the other way around.

Quote
And if that still isn't enough to identify the issue, I'd definitely recommend giving  valgrind  a try
Are you sure, that valgrind surely will identify the problem, even if that AV does not occur?

There's no guarantee that valgrind will pick up anything, but it very well might uncover e.g. a hidden memory corruption bug that sometimes causes a segfault further down the line.
Besides, basic usage of valgrind isn't nearly as complicated as the wiki makes it out to be:

  • Install  valgrind  with  sudo apt install valgrind
  • Check the  -gv  (Generate code for valgrind) option in the project settings and recompile
  • Run your program like so:  valgrind ./azul487

Edit: Fixed list formatting
« Last Edit: September 02, 2025, 09:17:16 am by Khrys »

Hartmut

  • Hero Member
  • *****
  • Posts: 1000
Thanks a lot Khrys for this clarifications. Sorry for my late answer, a sister of mine is currently moving from Win10 to Linux and has plenty of problems and questions and needs very much support.

As I find the time, I will search my program and all used common units for Forms with a Memo with anchors from other controls to the Memo. And if using valgrind is as easy as you wrote (special thanks for that) I will try it too. If I have results, I will post them. But might need some days.

These 2 questions are still open:

What does Application.Components[] contain?
My suspicion is, that this array contains all GUI-Controls (?) which have been created with it's Parent = 'Application'.
Can somebody confirm, whether this is correct? Or give a better answer?

I want to add some more debug outputs at meaningful source positions, so that I get more detailed informations, if the AV occors again some day.
E.g. by improving procedure show_Application_Components() shown in reply #5, e.g. by trying to detect there, which of those following calls to 'Components[].Free' in procedure TApplication.DoBeforeFinalization() - also shown in reply #5 - will fail.
Are there any ideas for?

Thanks im advance.

egsuh

  • Hero Member
  • *****
  • Posts: 1694
From low-level programmer's view point,  most of Access Violations at closing forms I have experienced came from trying to do something with freed objects. So, if you are trying to save any data of components or objects to ini file, then it may cause AV. Or you should not free any components OWNed by other components --- of course I think you already know this.

Thaddy

  • Hero Member
  • *****
  • Posts: 18306
  • Here stood a man who saw the Elbe and jumped it.
My suspicion is, that this array contains all GUI-Controls (?) which have been created with it's Parent = 'Application'.
It just contains the root components, e.g. form1. Then you can subsequently check the componentcount from form1, etc. so it works like a tree-like structure. Something like:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. var i,j:integer;
  3. begin
  4.   for i := 0 to Application.ComponentCount -1 do
  5.     begin
  6.       memo1.Lines.add(Application.Components[i].Name);
  7.       if Application.Components[i].ComponentCount > 0 then
  8.         for j := 0 to Application.Components[i].componentcount - 1 do
  9.           memo1.lines.Add(Application.Components[i].components[j].Name);
  10.     end;
  11. end;
But normally you would write a simple recursive function for that. This has only two dimensions.
(This is for e.g. a form with a button and a menu);
« Last Edit: September 03, 2025, 11:16:04 am by Thaddy »
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

Hartmut

  • Hero Member
  • *****
  • Posts: 1000
Thanks to egsuh and Thaddy for your posts.

From low-level programmer's view point,  most of Access Violations at closing forms I have experienced came from trying to do something with freed objects.
I will check this, but will need some time.

Quote
So, if you are trying to save any data of components or objects to ini file, then it may cause AV.
I checked this. The INI-file contains only some GUI-Controls, which are part of the Main-Form 'Form1'. The are all included in the corresponding *.lfm file, so I think that they are created and free'd automatically. And the AV occured, after the INI-file was written completely and correctly. So I think, there is no problem.

Quote
Or you should not free any components OWNed by other components --- of course I think you already know this.
My knowledge about free'ing classes correctly is low. I found a source position, where a Form is free'd, which had been created via "FormPV:=TFormPV.CreateNew(Application)". I will test, if free'ing this Form will delete this Form from Application.Components[]. If not, that should be wrong (from my today understanding) and might be the root cause for the AV. But this test will need some time, because currently I have a lot of other things to do.

My suspicion is, that this array contains all GUI-Controls (?) which have been created with it's Parent = 'Application'.
No, it just contains the root components, e.g. form1.
Please can you explain: what exactly are "root components"?

Thaddy

  • Hero Member
  • *****
  • Posts: 18306
  • Here stood a man who saw the Elbe and jumped it.
root = top-level.
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

Hartmut

  • Hero Member
  • *****
  • Posts: 1000
root = top-level.
Sorry, I don't understand. What exactly do you mean with 'top-level' components?
 - what makes a component 'top-level'? Can you please define this?
 - and does Application.Components[] contain only components, which are currently 'active'?
 - what about a component, which had been closed, but not free'd?
 - what about a component, which had been free'd?
Please be more elaborately that I can understand.

Thaddy

  • Hero Member
  • *****
  • Posts: 18306
  • Here stood a man who saw the Elbe and jumped it.
Application has components (top-level) components have components  (second level) and these components can have components (third-level) etc, etc.
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

wp

  • Hero Member
  • *****
  • Posts: 13199
I never had to access Application.Components[] (I did not even know that it exists...). Fiddling in there without knowing what you are doing increases the chance that you may damage something...

My program always writes some debug infos into a terminal. The last 2 lines before the AV were:
Code: Text  [Select][+][-]
  1. TForm1.FormClose() finished
  2. TForm1.IniPropStorage1SavingProperties() started
  3. [FORMS.PP] ExceptionOccurred
  4. ...
That means: event FormClose() of the main form had run completely and the event to write the INI-file was started correctly. There is currently no debug message when this event has run completely, but the INI-file was written correctly and had a current timestamp. So the AV must have occured "very late".
I would not swear on that. TIniFile is written immediately, you cannot conclude from the existance of a valid and recent ini file that it has been written to the end:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
  2. var
  3.   ini: TIniFile;
  4. begin
  5.   ini:= TIniFile.Create('test.ini');
  6.   try
  7.     ini.WriteString('test', 'string', 'abc');
  8.     ini.WriteFloat('test', 'float', 1.0);
  9.     ini.WriteDateTime('test', 'error', StrToDateTime('aaaa'));
  10.     ini.WriteString('test', '2nd string','def');
  11.   finally
  12.     ini.Free;
  13.   end;
  14. end;
This code raises an exception while writing some item to the ini file. When your code proceeds afterwards you will find a valid ini file on your disk nevertheless (of course missing the last lines).

Why don't you add a debug line at the end of the writing procedure?

It could be that the inipropstorage wants to write properties of a component which already has been destroyed under some circumstances. Deactivate the inipropstorage mechanism for the moment and watch whether the crash is happening again.
« Last Edit: September 03, 2025, 12:10:47 pm by wp »

Hartmut

  • Hero Member
  • *****
  • Posts: 1000
Application has components (top-level) components have components  (second level) and these components can have components (third-level) etc, etc.
Hello Thaddy,
thanks that you want to help me, but - as often - you do not help me really, because you give no real answers to my questions:
 - I know, that Application.Components[] contains a tree of Components.
 - But what I want to know is, which Components are contained there? Which is the rule, whether and when a Component is added to this array? And which is the rule, whether and when a Component is deleted from this array? Was my question, which I repeated multiple times with different wordings, not clear enough?
 - And from 4 questions in my last post you only answered one...



Hello wp, thanks for your post.

I never had to access Application.Components[] (I did not even know that it exists...). Fiddling in there without knowing what you are doing increases the chance that you may damage something...
I do not want to change anything in Application.Components[]. I only want to display - after Application.Run() has finished - several infos from its content, which might help, if the AV occurs again some day.

Quote
TIniFile is written immediately, you cannot conclude from the existance of a valid and recent ini file that it has been written to the end:
...
This code raises an exception while writing some item to the ini file. When your code proceeds afterwards you will find a valid ini file on your disk nevertheless (of course missing the last lines).
I made a copy of the INI-file shortly after the AV occured 1 week ago. I checked it again: it is complete (not even 1 line is missing).

Quote
Why don't you add a debug line at the end of the writing procedure?
I'm still working on adding several more debug outputs. Adding a debug line at the end of the INI-writing procedure is already done. I'm still working to improve my procedure show_Application_Components() shown in reply #5.

Quote
It could be that the inipropstorage wants to write properties of a component which already has been destroyed under some circumstances.
The INI-file contains only some GUI-Controls, which are part of the Main-Form 'Form1'. They are all included in the corresponding *.lfm file, so I think that they are created and free'd automatically.

Quote
Deactivate the inipropstorage mechanism for the moment and watch whether the crash is happening again.
This AV occured only once in more than 1 year. Plenty of new runs did not occur it again. I would have to deactivate the inipropstorage mechanism probably for a very long time. And as long as the AV doesn't occur again, I would never know, if the AV was caused by the inipropstorage writing mechanism or not, so I could never repair it.
I think it's better, to let it be active, but with the mentioned debug output when it has completed. If the inipropstorage writing mechanism would ever cause an AV, then I could identify this surely.

Thaddy

  • Hero Member
  • *****
  • Posts: 18306
  • Here stood a man who saw the Elbe and jumped it.
Hello Thaddy,
thanks that you want to help me, but - as often - you do not help me really, because you give no real answers to my questions:
But I think I did. You can get at all the component properties, like name, classname, parent, whatever.
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

 

TinyPortal © 2005-2018