Recent

Author Topic: Closing TAB raises a RefCount error.  (Read 861 times)

Ñuño_Martínez

  • Hero Member
  • *****
  • Posts: 1186
    • Burdjia
Closing TAB raises a RefCount error.
« on: May 08, 2022, 12:24:42 pm »
Lets picture it:

I have a window with a TPageControl. Each tab has a custom TFrame inside.  Te main form has a button to close the current Tab, and does it through a TAction.  The TFrame has a pop-up menu that includes an option that uses the same TAction in the main form to close itself.

The thing is that if I use the button then the Tab is closed and destroyed, and there's no problem in it.  But when using the pop-up menu option a fatal error raises an the application is freezed (even runing in Lazarus).  Runing from a console it also shows a bunch of errors:
Code: [Select]
(mlsde:2707): Pango-WARNING **: 11:49:17.500: Invalid UTF-8 string passed to pango_layout_set_text()

(mlsde:2707): Pango-WARNING **: 11:49:18.340: Invalid UTF-8 string passed to pango_layout_set_text()

(mlsde:2707): Pango-WARNING **: 11:49:18.741: Invalid UTF-8 string passed to pango_layout_set_text()

(mlsde:2707): Pango-WARNING **: 11:49:19.542: Invalid UTF-8 string passed to pango_layout_set_text()

(mlsde:2707): Pango-WARNING **: 11:49:19.943: Invalid UTF-8 string passed to pango_layout_set_text()
WARNING: TMenuItem.Destroy with LCLRefCount>0. Hint: Maybe the component is processing an event?
WARNING: TSynEdit.Destroy with LCLRefCount>0. Hint: Maybe the component is processing an event?

From the latest lines I suspect that the problem is I'm trying to destroy the pop-up menu while it is processing the event itself.

This is the event code:
Code: Pascal  [Select][+][-]
  1. (******** In the main form *********)
  2.  
  3. (* Executes an environment action. *)
  4.   procedure TMainWindow.ActionEnvironmentExecute (Sender: TObject);
  5.   begin
  6.     case (Sender as TComponent).Tag of
  7.    { ... Other tags (i.e. actions) ... }
  8.     tagCloseAllTabs:
  9.       Self.CloseAllTabs;
  10.     tabCloseCurrentTab:
  11.       Self.CloseCurrentTab;
  12.     otherwise
  13.     { This should never be rendered, so no translation required. }
  14.       ShowError ('Action environment tag: %d', [TComponent (Sender).Tag]);
  15.     end
  16.   end;    
  17.  
  18.  
  19.  
  20. (* Closes current tab. *)
  21.   procedure TMainWindow.CloseCurrentTab;
  22.   begin
  23.   { Be sure there's a tab open. }
  24.     if Assigned (Self.EditorList.ActivePage) then
  25.     begin
  26.       Self.FindEditorInTab (Self.EditorList.ActivePage).CloseTab;
  27.       Self.UpdateFileComponentStates
  28.     end
  29.   end;
  30.  
  31.  
  32. (******** In the TFrame *********)
  33.  
  34. (* Closes tab. *)
  35.   procedure TSourceEditorFrame.CloseTab;
  36.   begin
  37.     if Self.CanClose then TTabSheet (Self.Parent).Free
  38.   end;
  39.  

Is that the problem?  How can I do it?  I know it is possible because Lazarus does it (I tried to find it but I can't, still looking for it).
« Last Edit: May 08, 2022, 12:27:56 pm by Ñuño_Martínez »
Are you interested in game programming? Join the Pascal Game Development community!
Also visit the Game Development Portal

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9754
  • Debugger - SynEdit - and more
    • wiki
Re: Closing TAB raises a RefCount error.
« Reply #1 on: May 08, 2022, 01:17:58 pm »
Well the context menu is (from what I can see) bound to the SynEdit, inside the frame, inside the Tab.

So, "TMainWindow.ActionEnvironmentExecute" is called by code in the above listed components.
When "TMainWindow.ActionEnvironmentExecute" finishes, it returns to the caller (and therefore to the PopUpMenu and the SynEdit).

I assume that CloseCurrentTab/CloseAllTab  will Destroy/Free the tabsheet?

Well if you destroy the TabSheet, you also destroy all components in it...
But if inside "TMainWindow.ActionEnvironmentExecute" you destroy the PopUpMenu and SynEdit (in the tabsheet), and then you return, then the code of that PopUpmenu/SynEdit act on destroyed objects => and they will likely crash (and that crash would just be an access violation, without any explanation).

LCLRefCount catches that early, and gives an error that points to the problem. As you noticed the error says: "Maybe processing an event"


So basically you can not Destroy the TabSheet (or anything containing the calling component) in you event.
Instead you should:
- Hide it (Visible := False)
- Application.ReleaseComponent(TheTabSheet__Or__Whatever_you_want_to_free);

That will call destroy later, when it will be safe.
« Last Edit: May 08, 2022, 01:20:48 pm by Martin_fr »

korba812

  • Sr. Member
  • ****
  • Posts: 390
Re: Closing TAB raises a RefCount error.
« Reply #2 on: May 08, 2022, 01:18:29 pm »
Use Application.QueueAsyncCall() in popup menu click event with method that closes the tab.
Code: Pascal  [Select][+][-]
  1. procedure TForm1.MenuItem1Click(Sender: TObject);
  2. begin
  3.   Application.QueueAsyncCall(@CloseTab, 0);
  4. end;
  5.  
  6. procedure TForm1.CloseTab(Data: PtrInt);
  7. begin
  8.   ActionCloseTab.Execute;
  9. end;
  10.  

Ñuño_Martínez

  • Hero Member
  • *****
  • Posts: 1186
    • Burdjia
Re: Closing TAB raises a RefCount error.
« Reply #3 on: May 08, 2022, 07:29:24 pm »
Thank-you both @Martin_fr and @korba812.  I'll try both ways later and see which one fits better with my application. :)
Are you interested in game programming? Join the Pascal Game Development community!
Also visit the Game Development Portal

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9754
  • Debugger - SynEdit - and more
    • wiki
Re: Closing TAB raises a RefCount error.
« Reply #4 on: May 08, 2022, 07:47:29 pm »
Application.ReleaseComponent does a QueueAsyncCall.

The difference is:

If you to "QueueAsyncCall(@CloseTab, 0);" you defer the entire procedure.
If you use  ReleaseComponent you only defer the destroy.

Normally an ASync-call is executed within a millisecond. That is as soon as the event has been handled, it will be executed.
But => not always.
- Under extremely heavy load conditions other events may keep deferring the async execution. This is rare, but....
- IIRC in modal forms there were some issues. But I am not 100% sure.


Thus, if you do
Code: Pascal  [Select][+][-]
  1. // Somewhere in your event
  2. TabsheetToBeClosed.Visible := False; // will be executed immediately
  3. Application.ReleaseComponent(TabsheetToBeClosed); // will internally do Application.QueueAsyncCall(@Internally_Call_Destroy, 0);
then you always have an immediate visual reaction on the users input.



Ñuño_Martínez

  • Hero Member
  • *****
  • Posts: 1186
    • Burdjia
Re: Closing TAB raises a RefCount error.
« Reply #5 on: May 09, 2022, 11:11:33 am »
Thankyou very much for the advice.  Now the bug is fixed and works as expected. :)
Are you interested in game programming? Join the Pascal Game Development community!
Also visit the Game Development Portal

 

TinyPortal © 2005-2018