Forum > LCL

Closing TAB raises a RefCount error.

(1/2) > >>

Ñuño_Martínez:
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: ---(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?
--- End code ---

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  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---(******** In the main form *********) (* Executes an environment action. *)  procedure TMainWindow.ActionEnvironmentExecute (Sender: TObject);  begin    case (Sender as TComponent).Tag of   { ... Other tags (i.e. actions) ... }    tagCloseAllTabs:      Self.CloseAllTabs;    tabCloseCurrentTab:      Self.CloseCurrentTab;    otherwise    { This should never be rendered, so no translation required. }      ShowError ('Action environment tag: %d', [TComponent (Sender).Tag]);    end  end;        (* Closes current tab. *)  procedure TMainWindow.CloseCurrentTab;  begin  { Be sure there's a tab open. }    if Assigned (Self.EditorList.ActivePage) then    begin      Self.FindEditorInTab (Self.EditorList.ActivePage).CloseTab;      Self.UpdateFileComponentStates    end  end;  (******** In the TFrame *********) (* Closes tab. *)  procedure TSourceEditorFrame.CloseTab;  begin    if Self.CanClose then TTabSheet (Self.Parent).Free  end; 
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).

Martin_fr:
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.

korba812:
Use Application.QueueAsyncCall() in popup menu click event with method that closes the tab.

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---procedure TForm1.MenuItem1Click(Sender: TObject);begin  Application.QueueAsyncCall(@CloseTab, 0);end; procedure TForm1.CloseTab(Data: PtrInt);begin  ActionCloseTab.Execute;end; 

Ñuño_Martínez:
Thank-you both @Martin_fr and @korba812.  I'll try both ways later and see which one fits better with my application. :)

Martin_fr:
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  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---// Somewhere in your eventTabsheetToBeClosed.Visible := False; // will be executed immediatelyApplication.ReleaseComponent(TabsheetToBeClosed); // will internally do Application.QueueAsyncCall(@Internally_Call_Destroy, 0);then you always have an immediate visual reaction on the users input.


Navigation

[0] Message Index

[#] Next page

Go to full version