Lazarus

Programming => General => Topic started by: superc on May 12, 2021, 10:09:26 am

Title: [SOLVED] SIGSEGV on free pointer Tframe
Post by: superc on May 12, 2021, 10:09:26 am
Hello,
I want to create dinamically tframe on  tpanel and free it for loading another; I use this code for create TFrame:

Code: Pascal  [Select][+][-]
  1. procedure TMonitorForm.Button1Click(Sender: TObject);
  2. var
  3.   hwnd: TFrame;
  4. begin
  5.   hwnd := CreateFrameAndHostPanel(pnlRight, pnlRight, pnlRight);
  6.   pntFrame := @hwnd;
  7.   ShowMessage(IntToStr(TFrameLEDMonitor(pntFrame^).getNumber));
  8.  
  9.   TFrameLEDMonitor(pntFrame^).Free;        
  10.  
  11. end;  
  12.  
  13. function TMonitorForm.CreateFrameAndHostPanel(_Owner: TComponent; _Parent: TWinControl; _Panel: TPanel): TFrame;
  14. begin
  15.   Result := FrameLcd.TFrameLCDMonitor.Create(Owner);
  16.   Try
  17.     Result.Parent := _Parent;
  18.   Except
  19.     FreeAndNil(Result);
  20.     raise;
  21.   End;
  22. end;  
  23.  
  24.  

In this code Free works well and free pointed frame, but if I move free line of code in another button like as:
Code: Pascal  [Select][+][-]
  1. procedure TMonitorForm.Button2Click(Sender: TObject);
  2. begin
  3.   TFrameLEDMonitor(pntFrame^).Free;
  4. end;      
  5.  

I get and SIGSEGV error and access violation; pntFrame is declared in public section and is typed as:

Code: Pascal  [Select][+][-]
  1. type
  2.   pointerTFrame = ^TFrame;                                      
  3.  

I don't understand why,

thanks in advance.
Title: Re: SIGSEGV on free pointer Tframe
Post by: marcov on May 12, 2021, 10:25:48 am
You specified an owner when creating the frame. An owner (I assume the form) frees the owned class (the frame) on shutdown.

IIRC you can remove the owner registration using TComponent.RemoveComponent
Title: Re: SIGSEGV on free pointer Tframe
Post by: superc on May 12, 2021, 10:29:07 am
Excuse Marcov, I don't understand; i must remove owner as default passed with RemoveComponent?

Thanks
Title: Re: SIGSEGV on free pointer Tframe
Post by: howardpc on May 12, 2021, 10:38:33 am
Try declaring your public instance reference simply as
Code: Pascal  [Select][+][-]
  1. tempFrame: TFrameLEDMonitor;
and drop the local variable from your click event handler:
Code: Pascal  [Select][+][-]
  1. procedure TMonitorForm.Button1Click(Sender: TObject);begin
  2.   tempFrame := CreateFrameAndHostPanel(...);
  3.   ShowMessage(...);
  4.   tempFrame.Free;  tempFrame := Nil;
  5. end;
  6.  
Title: Re: SIGSEGV on free pointer Tframe
Post by: superc on May 12, 2021, 11:04:52 am
Try declaring your public instance reference simply as
Code: Pascal  [Select][+][-]
  1. tempFrame: TFrameLEDMonitor;
and drop the local variable from your click event handler:
Code: Pascal  [Select][+][-]
  1. procedure TMonitorForm.Button1Click(Sender: TObject);begin
  2.   tempFrame := CreateFrameAndHostPanel(...);
  3.   ShowMessage(...);
  4.   tempFrame.Free;  tempFrame := Nil;
  5. end;
  6.  

Works fine with
Code: Pascal  [Select][+][-]
  1.   public
  2.     { public declarations }
  3.     pntFrame: TFrame;      
  4.  

I've 5 Frame different, Thank you very much...






Title: Re: SIGSEGV on free pointer Tframe
Post by: marcov on May 12, 2021, 11:22:21 am
First, you need to fully understand the concepts of owner and parent.

A component has an owner and parent property, both of which can be NIL (unassigned).

If you set the owner of component A to component B then B will free A when B shuts down. So owner is only about memory safety.

For the rest, that's where the parent comes in. Most importantly, the "parent" is generally how events flow and the order of form drawing. From parent to child, to the child's children etc.


Keeping this in mind, your CreateandFrameHostPanel is a bit weird, in 3 ways even:

Code: [Select]
function TMonitorForm.CreateFrameAndHostPanel(_Owner: TComponent; _Parent: TWinControl; _Panel: TPanel): TFrame;
begin
  Result := FrameLcd.TFrameLCDMonitor.Create(Owner);
  Try
    Result.Parent := _Parent;
  Except
    // owner.RemoveComponent(Result)
    FreeAndNil(Result);
    raise;
  End;
end;   


If you frame must be shown on top of some other component (e.g. a TPanel called "panel1") it is logical to make panel1 the owner and parent of the create form.
Title: Re: SIGSEGV on free pointer Tframe
Post by: Warfley on May 12, 2021, 04:55:53 pm
while howardpc described a solution, I think there should be an explaination why the error occured in the first place.

First of all this has absolutely nothing to do with the owner, and a lack of RemoveComponent. Any component will call RemoveComponent on Free, so when handling components dynamically you don't need to care about the owner at all, it will be done all internally.

The problem is here:
Code: Pascal  [Select][+][-]
  1. procedure TMonitorForm.Button1Click(Sender: TObject);
  2. var
  3.   hwnd: TFrame;
  4. begin
  5.   hwnd := CreateFrameAndHostPanel(pnlRight, pnlRight, pnlRight);
  6.   pntFrame := @hwnd;
Here you assign a global variable the pointer of a local variable. As soon as this procedure finishes the local variable will go out of scope and any pointer to it, will be invalid, a so called dangling pointer.

When calling free from the other event handler:
Code: Pascal  [Select][+][-]
  1. procedure TMonitorForm.Button2Click(Sender: TObject);
  2. begin
  3.   TFrameLEDMonitor(pntFrame^).Free;
  4. end;
The previous event has already finished, meaning the pointer is dangling and when calling free, you dereference an invalid pointer. The segfault that occurs is because the memory referenced is invalid. In fact it is not because the pntFrame is dangling, your computer can not detect that, but a TFrame is also just a pointer and therefore the derefencing of Free causes the error because the invalid pointer points to something basically "random".

Long story short, never put pointer to local variables into global variables. Second, TFrame is already a pointer, all classes are hidden pointers in Pascal, so best is to skip the middle man and use TFrame directly instead of a pointer to TFrame
Title: Re: SIGSEGV on free pointer Tframe
Post by: superc on May 14, 2021, 08:42:59 am
while howardpc described a solution, I think there should be an explaination why the error occured in the first place.

First of all this has absolutely nothing to do with the owner, and a lack of RemoveComponent. Any component will call RemoveComponent on Free, so when handling components dynamically you don't need to care about the owner at all, it will be done all internally.

The problem is here:
Code: Pascal  [Select][+][-]
  1. procedure TMonitorForm.Button1Click(Sender: TObject);
  2. var
  3.   hwnd: TFrame;
  4. begin
  5.   hwnd := CreateFrameAndHostPanel(pnlRight, pnlRight, pnlRight);
  6.   pntFrame := @hwnd;
Here you assign a global variable the pointer of a local variable. As soon as this procedure finishes the local variable will go out of scope and any pointer to it, will be invalid, a so called dangling pointer.

When calling free from the other event handler:
Code: Pascal  [Select][+][-]
  1. procedure TMonitorForm.Button2Click(Sender: TObject);
  2. begin
  3.   TFrameLEDMonitor(pntFrame^).Free;
  4. end;
The previous event has already finished, meaning the pointer is dangling and when calling free, you dereference an invalid pointer. The segfault that occurs is because the memory referenced is invalid. In fact it is not because the pntFrame is dangling, your computer can not detect that, but a TFrame is also just a pointer and therefore the derefencing of Free causes the error because the invalid pointer points to something basically "random".

Long story short, never put pointer to local variables into global variables. Second, TFrame is already a pointer, all classes are hidden pointers in Pascal, so best is to skip the middle man and use TFrame directly instead of a pointer to TFrame

Now is all clear,
however Owner in this function I removed it from the code because it is useless,

thank you all.









TinyPortal © 2005-2018