 |
BorlandTalk.com Borland discussion newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Paul E. Schoen Guest
|
Posted: Fri Jan 13, 2006 11:34 pm Post subject: TForm OnClose and OnDestroy (and other help needed) |
|
|
I am trying to clean up my application by dynamically creating, hiding, and
closing several forms rather than using the autocreate. I am using Delphi 4.
I used the following to create or show the form:
procedure TForm1.DataButtonClick(Sender: TObject);
begin
If ReclData = nil then
ReclData := TReclData.Create(self)
else
ReclData.Visible := True;
end;
In the form, I allow the user to hide the form, and show it again as needed
from the main form. This works OK.
If the user decides to close the form, I used:
procedure TReclData.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
end;
However, upon the next DataButton click, I got access errors, because the
form was not nil. I had to add:
procedure TReclData.FormDestroy(Sender: TObject);
begin
ReclData := nil;
end;
This seems to have fixed the problem, but I don't know why the caFree action
does not also do this.
Also (finally) do I need to add cleanup code to free ReclData when the
program terminates?
I am relatively new to Delphi and OOP, and want to make sure I understand
these concepts. The executable package is available at www.Ortmaster.com if
you would like to see what I'm trying to do. I can provide a link to my
entire project code but I don't want to publicize it except to individuals.
If you are interested in reviewing the code and helping me make this
application run smoothly and be reliable, I am willing to compensate you for
your time. It is a lot of code, and much of it was written before I had even
a fair understanding of OOP.
Thanks!
--
Paul E. Schoen, President
P S Technology, Inc.
Cockeysville, MD
www.pstech-inc.com
|
|
| Back to top |
|
 |
Rob Kennedy Guest
|
Posted: Sat Jan 14, 2006 4:21 am Post subject: Re: TForm OnClose and OnDestroy (and other help needed) |
|
|
Paul E. Schoen wrote:
| Quote: | In the form, I allow the user to hide the form, and show it again as needed
from the main form. This works OK.
If the user decides to close the form, I used:
|
Will the user be able to distinguish between closing a window and hiding
it? What difference will there be, to the user? What significance will
it have to the way your program operates?
| Quote: | procedure TReclData.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
end;
However, upon the next DataButton click, I got access errors, because the
form was not nil.
|
A form is never nil. A *variable* can be nil. ReclData is not a form.
It's a variable that holds a reference to a form -- to an instance of
the TReclData class.
| Quote: | I had to add:
procedure TReclData.FormDestroy(Sender: TObject);
begin
ReclData := nil;
end;
This seems to have fixed the problem, but I don't know why the caFree action
does not also do this.
|
Because the TReclData class doesn't know about the ReclData variable.
Keep in mind that you could have *any number* of variables *all*
referring to that single form instance. But that instance does know
about *any* of them, unless you tell it. In the OnDestroy handler above,
you've told it about the ReclData variable.
| Quote: | Also (finally) do I need to add cleanup code to free ReclData when the
program terminates?
|
You don't really *need* to, but it's good practice to clean up after
yourself. Free whatever you created.
--
Rob
|
|
| Back to top |
|
 |
alanglloyd@aol.com Guest
|
Posted: Sat Jan 14, 2006 2:57 pm Post subject: Re: TForm OnClose and OnDestroy (and other help needed) |
|
|
Additional to Rob's excellent points. If you're into creating and
freeing objects you need to know when a variable references a valid
object. A convenient way of doing this is to set the variable value to
nil when you Free it.
The Free method of an object checks for non-nullity before calling the
object's Destroy destructor. For later Delphis there is a FreeAndNil
method.
There are two other issues. One is that if you create an object and set
the Owner (responsible for memory allocation) to a valid object then
that owner will destroy the object when the Owner is Free(d) - ie at
end of application. If you have already Free(d) it you might get an AV
when the Owner tries to free a non-valid object. I tend to set the
Owner to nil on creation, and Free (and nil) the object myself in code.
Secondary, you appear not to have set ReclData's Parent. The Parent of
a TControl's descendant is the object which is the source (and route)
of windows' messages and on which it is visually placed. If you don't
set the Parent, the object may not display itself. (see also Delphi
Help on Parent, and note the difference between Owner and Parent).
Alan Lloyd
|
|
| Back to top |
|
 |
Maarten Wiltink Guest
|
Posted: Sat Jan 14, 2006 3:55 pm Post subject: Re: TForm OnClose and OnDestroy (and other help needed) |
|
|
<alanglloyd (AT) aol (DOT) com> wrote
[...]
| Quote: | The Free method of an object checks for non-nullity before calling the
object's Destroy destructor. For later Delphis there is a FreeAndNil
method.
|
FreeAndNil is not a method, it's a procedure in SysUtils. This is for
convenience, so you can call it without qualifying it with anything.
Personally, I don't like FreeAndNil. Due to the semantics of var
parameters, the parameter is untyped. That makes it less safe. It will
not complain if you pass it something that isn't an object.
I also, strongly, feel that it exists _only_ for convenience where none
is really needed, and that it sends the wrong message about keeping
track of your object references. If you free an object, _all_ pointers
to it should go away. The existence alone of FreeAndNil suggests that
clearing the pointer you use to free it through is enough. In real
programs, there may be any number of aliases, and they should all be
thrown away when the objects dies. It is not feasible to write a single
procedure for _that_ that works all the time. Most particularly,
FreeAndNil is not it. FreeAndNil addresses a much simpler case.
As a final point, sometimes it isn't necessary to clear the pointer at
all. This was pointed out to me right here, and frankly it came as a
bit of a shock. It was in fact one of the things that made me review,
and improve, my understanding of reference management.
The particular case where FreeAndNil is superfluous is where the only
reference to an object lives in a local variable that is about to go
out of scope. This may happen for example in displaying a modal dialog
box:
function ExecuteSomeDialog: Boolean;
var Form: TForm;
begin
Form:=TSomeDialogForm.Create(...);
try
Result:=(Form.ShowModal=mrOk);
finally
Form.Free;
end;
end;
The Form variable is proveably never used again and there is no call
whatsoever to clear it. The compiler will even issue a warning if you
do.
| Quote: | There are two other issues. One is that if you create an object and set
the Owner (responsible for memory allocation) to a valid object then
that owner will destroy the object when the Owner is Free(d) - ie at
end of application.
|
Not just any object. This is introduced in TComponent. And in scenarios
like the one above, it may result in destruction well before application
shutdown. But autocreated forms will have the Application object as
their Owner, and Delphi's runtime system will free the Application
object as part of program shutdown. This ripples through every
autocreated form and all components either designed onto each one, or
created at runtime and set to have the form as its owner.
| Quote: | If you have already Free(d) it you might get an AV
when the Owner tries to free a non-valid object. I tend to set the
Owner to nil on creation, and Free (and nil) the object myself in code.
|
I do that, too, but only for symmetry. I simply like it better when
there is visible code for both object construction and destruction,
because I know that that is the rule: clean up whatever you made. But
setting an Owner works just as well, you just don't _see_ the code that
cleans up.
There will never be any double-free problems due to manually freeing an
owned component. An owned component not only registers itself with its
owner, it also unregisters itself when it is freed. That makes it safe
to free an owned component outside the ownership system.
Groetjes,
Maarten Wiltink
|
|
| Back to top |
|
 |
Hans-Peter Diettrich Guest
|
Posted: Sun Jan 15, 2006 2:01 am Post subject: Re: TForm OnClose and OnDestroy (and other help needed) |
|
|
Maarten Wiltink schrieb:
| Quote: | Personally, I don't like FreeAndNil. Due to the semantics of var
parameters, the parameter is untyped. That makes it less safe. It will
not complain if you pass it something that isn't an object.
|
You're right, FreeAndNil should become a compiler magic.
| Quote: | I also, strongly, feel that it exists _only_ for convenience where none
is really needed, and that it sends the wrong message about keeping
track of your object references. If you free an object, _all_ pointers
to it should go away. The existence alone of FreeAndNil suggests that
clearing the pointer you use to free it through is enough. In real
programs, there may be any number of aliases, and they should all be
thrown away when the objects dies.
|
FreeAndNil is useful for data structures like autocreated forms, where
typically only one object reference is stored, and the objects are only
created on demand. IMO not so a rare case in real programs.
DoDi
|
|
| Back to top |
|
 |
Paul E. Schoen Guest
|
Posted: Sun Jan 15, 2006 8:24 am Post subject: Re: TForm OnClose and OnDestroy (and other help needed) |
|
|
I appreciate the comments and suggestions offered. However, I'm not sure if
my specific questions were answered. My procedure is:
| Quote: |
procedure TForm1.DataButtonClick(Sender: TObject);
****** I am assuming this means that the message was sent from the |
Button(Object) on the main form (Form1)?
| Quote: | begin
If ReclData = nil then
****** Here I assume the pointer is always nil when declared. It is a global |
variable in unit OrtData.
| Quote: | ReclData := TReclData.Create(self)
****** At this point I believe the self refers to Form1. It is created |
Visible and shows properly
| Quote: | else
ReclData.Visible := True;
****** This just brings up the form again if the user has hidden it
end;
If the user decides to close the form, I used:
procedure TReclData.FormClose(Sender: TObject; var Action: TCloseAction);
****** This is triggered if the user closes the form rather than hiding it
begin
Action := caFree;
****** This causes the form to be freed and destroyed, but the pointer is |
not "nil"
| Quote: | end;
However, upon the next DataButton click, I got access errors, because the
form was not nil. I had to add:
procedure TReclData.FormDestroy(Sender: TObject);
begin
ReclData := nil;
****** I had to add this OnDestroy so I could tell if the form had been |
destroyed. When I examined ReclData before setting it nil, it showed the
entire object structure, but all properties were nil.
| Quote: | end;
This seems to have fixed the problem, but I don't know why the caFree
action
does not also do this.
****** Is there another way to determine if a variable references a |
destroyed form?
| Quote: |
Also (finally) do I need to add cleanup code to free ReclData when the
program terminates?
****** I think it is freed and destroyed when Form1 is closed (and |
application terminates). This should be true if Form1 is the Owner.
Hopefully, that was specified by (Self). Am I correct?
| Quote: |
I am relatively new to Delphi and OOP, and want to make sure I understand
these concepts. The executable package is available at www.Ortmaster.com
if
you would like to see what I'm trying to do. I can provide a link to my
entire project code but I don't want to publicize it except to
individuals.
If you are interested in reviewing the code and helping me make this
application run smoothly and be reliable, I am willing to compensate you
for
your time. It is a lot of code, and much of it was written before I had
even
a fair understanding of OOP.
|
****** This is still true. I do not have the time to become an expert
programmer, as I have other things I must do to complete this project, such
as hardware design, PC Board design, testing, documentation, and interfacing
to another application that is written in (UGH!) Visual Basic .Net (not my
decision). Any help will be appreciated. The application runs adequately but
the code is very klunky. There are several thousand lines of code, so I
realize the immensity of this project. However, I just need to have it
checked for major flaws and memory leaks. It has been a challenging (and
sometimes fun) project, but I need to finalize it in a form that is reliable
and easier to maintain.
| Quote: |
Thanks!
--
Paul E. Schoen, President
P S Technology, Inc.
Cockeysville, MD
www.pstech-inc.com
|
|
|
| Back to top |
|
 |
Maarten Wiltink Guest
|
Posted: Sun Jan 15, 2006 4:39 pm Post subject: Re: TForm OnClose and OnDestroy (and other help needed) |
|
|
"Paul E. Schoen" <pstech (AT) smart (DOT) net> wrote
| Quote: | procedure TForm1.DataButtonClick(Sender: TObject);
****** I am assuming this means that the message was sent from the
Button(Object) on the main form (Form1)?
|
No. It means that this method can be used for a TNotifyEvent, and
is syntactically in the TForm1 class, and nothing else. There may
be zero, one, or a dozen instances of TForm1, and the single Form1
variable may point to one, and if it does it may or may not be the
Application.MainForm.
The Sender parameter tells you where the message comes from. More than
one component throughout your application can link this event handler
to any of their TNotifyEvents, and firing any of those events will call
this method with Sender set appropriately. Or someone may decide to
call it directly with a nil Sender.
There are "standard", "normal", and "common" useage patterns, the most
common one of course being the one generated by the IDE when it's used
in a single event.
| Quote: | begin
If ReclData = nil then
****** Here I assume the pointer is always nil when declared. It is a
global variable in unit OrtData.
|
Global variables are _loaded_ (not exactly initialised) zeroed. Fields
are zeroed on object instantiation. Local variables contain garbage.
| Quote: | ReclData := TReclData.Create(self)
****** At this point I believe the self refers to Form1. It is
created Visible and shows properly
|
It will refer to _a_ TForm1 instance. Again, standard useage is to have
autocreated forms only, and then there will be exactly one, and the
Form1 variable will point to it.
| Quote: | else
ReclData.Visible := True;
****** This just brings up the form again if the user has hidden it
|
Yes. Standard form behaviour is to hide-on-close. But you've already
found the override for that, and are no longer dealing with standard
behaviour only (congratulations!).
| Quote: | end;
If the user decides to close the form, I used:
procedure TReclData.FormClose
(Sender: TObject; var Action: TCloseAction);
****** This is triggered if the user closes the form rather than
hiding it
|
The user, of course, can't hide the form. They can only ask to close
it. The default behaviour is to then hide the form but leave it in
existence. That means both the Delphi object and the actual Windows
window.
| Quote: | begin
Action := caFree;
****** This causes the form to be freed and destroyed, but the pointer
is not "nil"
|
Which pointer? The program doesn't know.
| Quote: | end;
However, upon the next DataButton click, I got access errors, because
the form was not nil.
|
Because a variable somewhere wasn't nil. The IDE knows about this
variable; the compiler doesn't. There's a big difference.
| Quote: | I had to add:
procedure TReclData.FormDestroy(Sender: TObject);
begin
ReclData := nil;
****** I had to add this OnDestroy so I could tell if the form had
been destroyed.
|
Not bad. You could easily have added this to the FormClose event handler,
and that would have been more obvious but less right. The reference
should be cleaned up when the object is destroyed, not when a window is
being closed/hidden.
| Quote: | When I examined ReclData before setting it nil, it
showed the entire object structure, but all properties were nil.
|
This was in the debugger? It was using its knowledge of the type of the
variable. The memory had been deallocated.
| Quote: | end;
This seems to have fixed the problem, but I don't know why the caFree
action does not also do this.
|
Because the compiler doesn't know.
Forms can be instantiated without leaving a reference in the form unit
variable. That is a valid and sometimes useful pattern, for example
when you're opening floating (non-modal) property dialogs. It's really
quite simple: sometimes, as a programmer, you have to write code for
things to happen. This seems to come as a shock to some people.
| Quote: | ****** Is there another way to determine if a variable references a
destroyed form?
|
NO. Once freed, you may no longer touch an object in any way. So yes,
all pointers to an object must be cleared immediately when the object
is freed.
| Quote: | Also (finally) do I need to add cleanup code to free ReclData when
the program terminates?
|
| Quote: | ****** I think it is freed and destroyed when Form1 is closed (and
application terminates). This should be true if Form1 is the Owner.
Hopefully, that was specified by (Self). Am I correct?
|
Yes.
Nice to be right, isn't it? (-:
Groetjes,
Maarten Wiltink
|
|
| Back to top |
|
 |
Paul E. Schoen Guest
|
Posted: Mon Jan 16, 2006 2:36 am Post subject: Re: TForm OnClose and OnDestroy (and other help needed) |
|
|
Thank you all for helping me understand the fairly basic concept of pointers
and the objects they may or may not reference. Also I now understand the
optional usage of the Sender parameter to determine which control triggered
the event handler. The Delphi help was useful for this. I suppose it is a
matter of programming style to use autogenerated forms which are also
automatically freed at termination, or else be concerned about cleaning up
any that are created by user-generated events. I was unaware that the
default form close behavior was hide, but that makes sense now. There is
probably no reason to create and destroy forms during program execution
unless there is a need for many copies, or if memory is *really* scarce.
| Quote: |
Thanks!
--
Paul E. Schoen, President
P S Technology, Inc.
Cockeysville, MD
www.pstech-inc.com
|
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|