I am new to Delphi and my question may be very basic.
I created a Form in a Delphi procedure. Until now, I was using ShowModal() and then freeing all the objects that I want to avoid leaking after closing the Form.
Now, I would like to show the Form modeless, but I don't know how I can free the objects inside the OnClose event.
Does anybody know a solution for it?
Simply set the Action parameter to caFree:
procedure TMyForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
end;
Per the documentation:
The value of the Action parameter determines if the form actually closes. These are the possible values of Action:
caNone
The form is not allowed to close, so nothing happens.
caHide
The form is not closed, but just hidden. Your application can still access a hidden form.
caFree
The form is closed and all allocated memory for the form is freed.
caMinimize
The form is minimized, rather than closed. This is the default action for MDI child forms.
Setting Action to caFree will cause the Form to call Release() on itself after the OnClose handler has exited:
Destroys the form and frees its associated memory.
Use Release to destroy the form and free its associated memory.
Release does not destroy the form until all event handlers of the form and event handlers of components on the form have finished executing. Release also guarantees that all messages in the form's event queue are processed before the form is released. Any event handlers for the form or its children should use Release instead of Free (Delphi) or delete (C++). Failing to do so can cause a memory access error.
Note: Release returns immediately to the caller. It does not wait for the form to be freed before returning.
Release() posts a delayed CM_RELEASE message to the Form window. Once execution flow returns to the main message loop and the message is dispatched, the Form will free itself from memory.
If your TForm object owns other objects, they will be freed automatically when the TForm is freed.
You can also
for each dynamically created object do
Object.Free;
Current Delphi versions (Since Xe) documentation recommends to use new approach
Object.DisposeOf;
This new approach works optimized in multi-device application (different operational system)
Related
I made my own UDP receiver a TComponent descendant. It has an OnReceive event. This event is used to add a line to TMemo which exists on the same form.
The problem is that when parent form is being destroyed TMemo gets destroyed first and UDP receiver continues to fire OnReceive event. Of course I get an exception when I try to mmo1.Lines.Add(S) to a non-existent Memo.
How to detect in TComponent the moment when parent form and it's components are about to be destroyed, but aren't destroyed yet? I would do then a proper receiver thread shutdown.
Why are you even trying to detect in your component when the parent form is destroyed? Wouldn't it be better to just utilize forms OnClose or OnCloseQuery events to stop your component from receiving data.
Both of these events are called before the form even begins destroying itself and its owned components. Where in OnCloseQuery event you can even prevent form from closing if certain work hasn't finished yet.
When you rely on components for your component to work but are not able to control their life-cycle, you can use TComponent's Notification to re-act when the component you are interested in gets destroyed.
First, your UDP Receiver will need to add itself to the notification list of the component with e.g.
Form.FreeNotification(Self);
to receive notifications from the TForm. Then, the UDP Receiver needs to override
procedure Notification(AComponent: TComponent; Operation: TOperation);
virtual;
There, you can listen to notifications where AComponent = Form and Operation = opRemove, that will indicate the Form is being removed.
Edit: Reading again, this might not be what you are interested in. If the OnReceive event is implemented in your form, you might check if the Form's ComponentState includes csDestroying.
Or, in case the Memo is created at design-time, or you explicitly create and free-and-nil the Memo yourself, just check if Memo <> nil.
I've read a LOT of stuff lately regarding this, but never found a final answer.
So, for example if I write:
Form1 := TForm1.Create(Application);
the aplication should be responsible for freeing the form from memory right?
why then people usually do as follows:
Form1 := TForm1.Create(Application);
Form1.ShowModal;
Form1.Free;
??
Saw in some places that if you try to "free" an object that was already freed you would get an EAccessviolation msg, but as I tested it is not always true.
So PLEASE, How this actually works??
This EAccessviolation thing is driving me crazy, how can I understand this thing completely?? where do I find this precious information!??
The general rules are:
If you're going to free it yourself, use nil as the owner.
If you're not going to free it yourself, assign an owner that will take the responsibility to free it.
So, if your code is something like this:
Form1 := TForm1.Create(...)
Form1.ShowModal;
Form1.Free;
You should write it with nil as the owner, and protect it in a try..finally block:
procedure TForm1.Button1Click(Sender: TObject);
var
AForm: TForm2;
begin
AForm := TForm2.Create(nil);
try
AForm.ShowModal;
finally
AForm.Free; // You know when it will be free'd, so no owner needed
end;
end;
If on the other hand, you're going to leave it around for a while, assign an owner that can take care of freeing it later:
procedure TForm1.Button1Click(Sender: TObject);
var
AForm: TForm2;
begin
AForm := TForm2.Create(Application);
// Here you don't know when it will be free'd, so let the
// Application do so
AForm.Show;
end;
Neither of these techniques will cause an access violation if done the way I've demonstrated here. Note that in both cases, I did not use the IDE-generated Form2 variable, but used a local one instead to avoid confusion. Those IDE-generated variables are evil (except for the required Form1 or whatever you name it that represents the main form, which must be auto-created and owned by the Application). Other than the var for the main form, I always delete that auto-generated variable immediately, and never auto-create anything except possibly a datamodule (which can be autocreated before the main form without any problem, as a datamodule cannot be the main form).
The task of a component's Owner is to destroy all owned components when you the owner are being destroyed.
The Application object is destroyed upon termination and so if you are relying on it to destroy your form, that won't happen until termination.
The key point here is the assigning an owner controls both who destroys the owned component, but also when it is destroyed.
In your case you have a modal form that you want to have a short life. Always write them like this:
Form := TMyModalForm.Create(nil);
try
Form.ShowModal;
finally
Form.Free;
end;
There's no point giving them an owner since you explicitly destroy it. And make sure that Form is a local variable.
It won't hurt particularly if you did pass an owner. It would just be wasteful as the owner was notified of its responsibility, and then notified that it was no longer responsible.
But if you did this:
Form := TMyModalForm.Create(Self);
Form.ShowModal;
then each time you showed the modal form you'd leak a form that would not be destroyed until the owning form was destroyed. If you made Application the owned, the modal forms would be leaked until termination.
Ownership between forms is reasonable for, say, the main form and a modeless relative that lives as long as the main form. The modeless form can be owned by the main form and then destroyed automatically when the main form is destroyed.
But if the main form holds a reference to the modeless form then I'd probably just have it unowned and explicitly destroyed from the main form's destructor.
#dummzeuch makes the good point that if you set Position to poOwnerFormCenter, then the framework expects you to provide a form as the owner. In my view, this is a poor design which conflates lifetime management with visual layout. But that is the design, so you are compelled to go along with it. There is though nothing to stop you explicitly destroying an owned component. You can do this:
Form := TMyModalForm.Create(Self); // Self is another form
try
Form.Position := poOwnerFormCenter;
Form.ShowModal;
finally
Form.Free;
end;
When you destroy the form, its owner is notified, and the destroyed form is removed the owner's list of owned components.
The main form itself is interesting. It has to be owned by Application since the main form has to be created by calling Application.CreateForm. That's the only time you should call Application.CreateForm. And the main form is usually the only form you should have owned by Application. Especially if you adopt the policy of having other forms unowned, or owned by the forms that spawn them.
But if you let the main form be destroyed at termination, when Application is destroyed, then you can bee caught out. I've experienced intermittent runtime errors at termination when coded that way. My remedy is to explicitly destroy the main form as the final act of the main .dpr file's body. That is, destroy the main form after Application.Run returns.
The only way I see is to add flag for this, but is this the best way?
When the form is destroyed and I check if(Assigned(form2)) the result is true? Why?
What is the way to do this?
You can use Form1.Showing to see if a form is closed or not.
Just closing a form does not free it unless you set Action := caFree in OnClose event. Default is caHide.
Wow, a blast from the past :)
The way that Assigned() works, is that it basically does nil check on the pointer. If you destroy form2, there will still be a memory address that form2 points to.
I has been a very long time since I've done any Delphi, but from memory, you need to manually set the form2 var to nil when it is destroyed. If you have a central place (eg. a form broker?) where you create & destroy forms, this should be quite easy.
If you use Form1.Free or Form1.Destroy, Delphi will destroy the object but wont set the object reference to nil. So instead use FreeAndNil.
For more information, check Andreas Rejbrand answer in this link
Faced with the same issue when doing some routine on closing application. In this case all forms are destroyed behind the stage but pointers are not set to nil. This code helphs me:
procedure TMyForm.FormDestroy(Sender: TObject);
begin
MyForm:=nil;
end;
So pointer becomes nil and I can check it with Assigned or compare to nil.
Just as a tip, the correct way to do it in some special cases is to create a timer that do the nil assign to the variable.
I will explain it (somehow it is complex), if you create your form within your own code MyForm:=TMyForm.Create and you have a MyFrom.Close it is very easy, just add a MyForm:=nil or also better MyForm.FreeAndNil... but sometimes the reference is not anyware.
Sample: You create inside a procedure, on a loop a lot of copies of the same form (or just one), let the form open and end that procedure, now the reference to the opened form is nowhere, so you can not assign nil or do a freeandnil, etc., in a normal way.
For that cases, the trick is to use a timer (of just one milisecond) that do it, that timer needs the reference, so you must store on a global like the reference to Self, all that can be done on the on close event.
The easiest way to do the free (when no reference anywhere) is to create a TObjectList on the main form, so it will hold all form references that needs to be free, and define a timer (one milisecond) that will go through that list doing the freeandnil; then on the onlcose you add Self to that list and enable that timer.
Now the other part, you have a normal form that is auto created on start, but you need to set it to nil and re-create it on your own code.
That case has a global that point to that form, so you only need to free and nil it, but NOT (i say it loud) on any part inside the own form code, you must do it OUT (i say if loud) of the form code.
Some times you will need to free the form, when user close it, and it is not shown in modal, this case is complex, but again the same trick is valid, on the onclose event you enable a timer (that is out of that form, normally on main form) adn that timer will free and nil. That timer interval can be set as just one milisecond, it will not be run until form has totally closed (please have in mind not using Application.ProcessMessages, that is normally a really bad idea).
If you set Self to nil, free or whatever inside the own form, you can corrupt your application memory (doing that is totally unsafe, not to mention it can eat ram).
The only way to free a form (and nil its reference), a form that is not shown as modal and is the user who close it, is to program a trigger that do that after the form is totally closed.
I know about setting action to do the free, but to set it to nil there is no other safe way.
Must say: If you use timers on your main form, run a Enabled:=False on all of them on the Onclose event... otherwise weird things may occur (not allways, but sometimes... race conditions about destroying application and running code on that timers), and of couse if some one was enabled act correctly to terminate it correctly or abort it, etc.
Your question is one of the complex things to do... free and nil a form that is closed not by code, but by user action.
For all the rest: Think like if the application has at the same time a lot of forms opened and all can interact with the user at the same time (anyone is modal), and you have code that references some of them from the others... you need to know f user has closed any form to avoid accesing that form from code. This is not trivial to be done unless you use timers.
If you have a 'central' form (like an MDI application) you can put that timer on the main MDI form, so any child form that is closed can be freed and nil, the trick is again a timer on that main form.
Only and only if you are sure you can free and nil all non visible forms, you can have a timer on the main form that goes through all forms and if Visible is false, then call FreeAndNil, i consider this way prone to errors, since if you add on a future a form that must not be freed but can stay hidden... this code will not be valid.
Allways remember that if is the user the onw who closes the form that must be freed and nil, there is no way on code to detect and act, no event is launched (after the form is totally closed) and before the form is totally closed you must not even try to free it or nil its reference, weird things can occur (more prone to that if motherboard has more than one socket, also more if your app uses threads, etc).
So, for threaded apps (and also not threaded) i use another method that works great and do not need timers, but need double checking prior to each ThatForm.*, the trick is to define a Form bolean public variable like as PleaseFreeAndNilMe on the public section on the form, then on the onclose (as last line) set it to True and on the OnCreate set it as False.
That way you will know if that form had been closed or only hidden (to hide a form never call close, just call hide).
So coded will look like (you can use this as a warper, instead of defining forms as TForm define them as TMyform, or also better, use a hack like type TForm=class(Forms.TForm) instead of TMyForm=class(TForm) just to have that variable added to all forms):
TMyForm=class(TForm)
...
public
PleaseFreeAndNilMe:=Boolean;
...
procedure TMyForm.FormCreate(Sender: TObject);
begin
PleaseFreeAndNilMe:=False;
...
end;
procedure TMyForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
...
PleaseFreeAndNilMe:=True;
end;
If you preffer hacked version:
TForm=class(Froms.TForm)
public
PleaseFreeAndNilMe:=Boolean;
end;
procedure TForm.FormCreate(Sender:TObject);
begin
inherited Create(Sender);
PleaseFreeAndNilMe:=False;
end;
procedure TForm.FormClose(Sender:TObject;var Action:TCloseAction);
begin
PleaseFreeAndNilMe:=True;
inherited FormClose(Sender,Action);
end;
But as i said, prior to access any member (or just where you do the nil compare) just call a 'global' function passign the reference (no matter if it was nil or not), coded as:
function IsNilTheForm(var TheForm: TMyForm);
begin
if nil=TheForm
then begin // The form was freed and nil
IsNilTheForm:=True; // Return value
end
else begin // The form refence is not nil, but we do not know is it has been freed or not
try
if TheForm.PleaseFreeAndNilMe
then begin // The form is not freed but wants to
try
TheForm.Free;
except
end;
try
TheForm:=Nil;
except
end;
IsNilTheForm:=True; // Return value
end
else begin // The form is not nil, not freed and do not want to be freed
IsNilTheForm:=False; // Return value
end;
except // The form was freed but not set to nil
TheForm:=Nil; // Set it to nil since it had beed freed
IsNilTheForm:=True; // Return value
end;
end;
end;
So where you do if nil=MyForm then ... you can now do if (IsNilTheForm(MyForm)) then ....
That is it.
It is better the timer solution, since form is freed as soon as possible (less ram used), with the PleaseFreeAndNilMe trick the form is not freed until IsNilTheForm is called (if you do not free it any where else).
That IsNilTheForm is so complex because it is considering all states (for a multi socket motherboard and threaded apps) and letting the code free / nil it anywhere else.
Of course, that function must be called on main thread and in atomic exclusion.
Free a Form and Nil its pointer is not a trivial thing, most when user can close it at any time (since no code out of the form is fired).
The big problem is: When a user closes a form there is no way to have a event handler that is triggered out of that form and after the form ends all things it is doing.
Imaigne now that the coder has put a lot of Application.ProcessMessages; every where on the app, also on that form, etc... and has not taken the care for race conditions... try to free and nil such a form after the user asks it to be closed... this is a nightmare, but can be solved with the hacked version of TForm that has a variable that tells that the form has not been freed but wants it.
Now imagine you use hacked TForm and want a normal TForm, just define it as ...= class(Forms.TForm), that way it will now have that extra variable., so calling IsNilTheForm will act as comparing to nil.
Hope this helps VCL coders to FIX such things, like raising an event when an object is destroyed, freed, niled, hide, etc... out of the code of that object, like on main form, etc. That would make live easier... or just fix it... Close and Free implies set to Nil all refences that point to it.
There is another thing that can be done (but i try to allways avoid it): Have multiple variables that point to the exact same form (not to copies ot it), that is prone to a lot of errors, you free one and need to nil all of them, etc... The code i show is also compatible with that.
I know the code is comples... but Free and Nil a form is more complex than my code.
Is there a failsafe way of freeing a Delphi control?
I have a TStringGrid descendent which I am "embedding" a custom control in it for an inplace editor. When the user navigates within the cells of the grid via the tab key or arrow keys, I need to create a dynamic control if the cell is editable. I have hooked the needed events and am utilizing the OnKeyDown event of my custom control to pass the navigation keys back to the parent TStringGrid.
Previously, the TStringGrid descendent would simply call FreeAndNil on the embedded control, but under some circumstances this would cause access violations inside of UpdateUIState/GetParentForm. Looking at the call stack, it appears that sometimes after the control was free'd, a WM_KEYDOWN (TWinControl.WMKeyDown) message was still happening.
I've all ready looked at and implemented the changes discussed in How to free control inside its event handler?. This appears to have resolved the issue, but I am wondering if there are any other cavet's to this approach.
In effect, this workaround has simply delayed the destruction of the control until after all the existing messages on the queue at the time the CM_RELEASE message was posted.
Would it not be possible that after the CM_RELEASE was posted, another WM_KEY* or similar message could all ready have been posted to the message queue?
My current CM_RELEASE handler looks like:
procedure TMyCustomControl.HandleRelease(var Msg: TMessage);
begin
Free;
end;
So, will this be safe in all instances or should I do something to clear any other messages from the queue? ( SendMessage(Self.Handle, WM_DESTROY, 0, 0) comes to mind )
In general you should not destroy a control in an event-handler of that control.
But since your function is a plain non virtual message handler which is never called from internal code in that control you should be ok. I don't like too too much from a style point of view, but I think it's ok for your use-case.
But a custom message might be cleaner.
Would it not be possible that after the CM_RELEASE was posted, another WM_KEY* or similar message could all ready have been posted to the message queue?
If messages in queue would cause big problems you could never safely destroy a control since messages can be posted from other threads and applications. Just make sure the correct functioning of your application doesn't depends on those messages being handles in every case.
SendMessage sends the message and wait for it to return, that's why you can not use it safely in the event handler of the control you are freeing.
PostMessage in the other hand, will send the message and will be processed after the event exits (if there are no more code in the event).
My Delphi 2010 application has a number of non-modal forms that are created and owned by the mainform. One of these forms has a formclose procedure that pops up a dialog asking the user if they want to save changes. If the user closes the mainform, the "owned" form's FormClose procedure is called, however the dialog is not shown, and the user has no chance to save.
Any suggestions? I can see the procedure is being called in the debugger, but it seems to just skip the dialog. Same thing happens with a showmessage. Does the owner form somehow override the actual showing of these dialogs?
Thanks
Rusty
That kind of thing should go in the OnCloseQuery event. Set CanClose to false in the handler to abort the closing (which is more or less standard: in these situations, Yes, No and Cancel are the usual answers, with Cancel aborting the closing process).
When the main form is closed then the application terminates which frees the main form which in turn frees the forms owned by it. The owned forms are not closed, just freed, therefore their OnClose event is normally not triggered at all.
If you see ShowMessage being called from the owned form's OnClose event but the dialog doesn't show up it's probably because the application is already terminated and no longer processing messages. This means that the owned form's OnClose event is triggered by somewhere in your own code but too late.
One way to reproduce this behaviour is to post WM_CLOSE message to the owned form from the main form's OnClose event. The message is then processed by the owned form at a later moment when the application is already terminated any attempt to call ShowMessage or any modal form has no effect anymore.
I agree with Michael that OnCloseQuery is better suited for the purpose of displaying a prompt to the user. Unfortunately this alone doesn't help since the owned forms are being freed not closed. You have to call their OnCloseQuery event manually, for example:
procedure TFormMain.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
var
I: Integer;
begin
CanClose := False;
for I := 0 to ComponentCount - 1 do
if Components[I] is TCustomForm then
if not TCustomForm(Components[I]).CloseQuery then
Exit;
CanClose := True; // or another check if the main form can be closed, too
end;
I recently ran into something along these lines. I found that simply adding the code:
if not Visible then
Show;
BringToFront;
right before the save changes dialog is displayed ends all confusion. The parent form is displayed if its not visible, and brought upward in zorder to the front of the pile, then on top of that is displayed the dialog.