Keith
What trouble are you having?
Any code in initialize gets executed when the program starts. Any code in
finalize gets executed when the program ends.
Rick Hansen
You can learn about Delphi startup & shutdown by reading the
SysInit and System units. I looked at the sequence a while ago
for Delphi 3.02; you can expect minor changes to occur between
versions but the essentials are unlikely to change much.
INITIALIZATION
--------------
After readying the exception handling mechanism Delphi calls
the initialization section for all units in DPR order.
This includes System and (usually) SysUtils and ActiveX units.
Note that by definition weak packaged units such as Windows,
CommCtrl and Ole2 have no initialization sections.
After initialization the DPR main program begins.
NORMAL TERMINATION SEQUENCE
---------------------------
Application.Run normally exits in the following manner:
- File|Exit calls main form's TCustomForm.Close.
- Close calls CloseQuery (which first calls OnCloseQuery for
each MDI child and then calls TMainForm.FormCloseQuery),
then calls TMainForm.OnClose(Self,caHide),
and lastly calls Application.Terminate.
- Terminate calls CallTerminateProcs, followed by
PostQuitMessage(0).
- PostQuitMessage places WM_QUIT in thread's message queue,
which asks TApplication.Run to leave its repeat..until Terminated
message loop.
Note that in D3 CallTerminateProcs was used only by the ComServ
unit. The procedure follows a similar concept to ExitProcs, and
is also called by TCustomForm.WMQueryEndSession.
WHEN ARE WINDOWS RESOURCES DESTROYED?
-------------------------------------
Those used to programming in C may be surprised to discover that
at this point window resources (and their corresponding VCL objects)
still have not been destroyed. Most Windows programs close by
calling DestroyWindow upon WM_CLOSE, and then in response to WM_DESTROY
call PostQuitMessage, finishing immediately the message loop quits.
In this manner window resources are usually destroyed well before
the main program exits, but Delphi does things differently.
In Delphi things are destroyed *after* the DPR main program ends.
At that point the System unit's _Halt0 routine:
- calls and frees any unit ExitProcs in reverse order of installation
- reports any runtime error if ErrorAddr <> nil, then clears ErrorAddr
- calls unit finalization in reverse order to initialization
It is during the finalization of Forms, and then Controls,
that Delphi destroys its windows resources.
The Forms unit calls DoneApplication if Application <> nil,
which performs
- ShowOwnedPopups(Handle), Application.ShowHints := False
- TComponent.Destroying := True (sets csDestroying flag)
- TComponent.DestroyComponents (removes/destroys owned components)
Note that this destroys TApplication's component windows.
The Controls unit finalization calls DoneControls,
which performs
- Application.Free, Application := nil
Note that the TApplication destructor performs:
- HELP_QUIT
- CancelHint
- DestroyWindow(FHandle)
- DoneCtl3D
- FIcon.Free
- FMenu.Free
- Screen.Free, Screen := nil
The TScreen destructor performs:
- Forms.Free, CustomForms.Free (reference lists)
- DestroyCursors
The above describes the usual application closure sequence as it
stood for D3. I haven't looked closely at D4 yet.
UNUSUAL TERMINATION SCENARIOS
-----------------------------
Notice that if a program calls Application.Terminate then it will
return from Application.Run without calling the form CloseQuery.
Three alternative means of program termination exist: ExitProcess,
Halt, and runtime error - none of which return from Application.Run.
There is no point using a try..finally block around the Run method
because exceptions never emerge from it.
- The ExitProcess API function stops the process cold without even
calling unit finalization. It leaves many libraries and resource
locks unreleased. Call this only if you have good reason.
- The Halt procedure bypasses both CloseQuery and remaining DPR code;
jumping to _Halt0 it immediately calls the ExitProcs and finalization.
- Runtime errors behave similar to Halt.
Termination state is held in the ErrorAddr and ExitCode variables.
Note in the _Halt0 code that the ErrorAddr variable is reported and
cleared after the ExitProcs are called but before finalization, which
gives the otherwise obsolete ExitProc a lasting role in error handling.
WHERE SHOULD RESOURCE CLEANUP BE PERFORMED?
-------------------------------------------
In traditional C-style Windows programming the WM_DESTROY handler
is a good place to release local resources. In Delphi the form
OnDestroy (or overriding the destructor) is a good place to free
things that were created and used by the form.
It's important to realise though that most windows and components
are destroyed AFTER most units are finalized. This means you
shouldn't use destructors to perform activity reliant on resources
freed by finalization. For example, suppose you had a unit like:
unit Log;
procedure LogActivity(s:string);
initialization
// open file or database to record log
finalization
// close file or database
end;
If you tried to make a log entry when each form was destroyed
you'll have grief because of form destruction occurring when the
Forms unit is finalized, which happens AFTER the log file is closed.
One solution for the above case is to perform logging in the form
CloseQuery instead of OnDestroy. Be aware though that both
Application.Terminate and Halt bypass CloseQuery and go straight
to finalization.
As a rule it's best to use destructors only to free local resources.
Wherever possible avoid unit interdependence in initialization/
finalization sections.
Most finalization problems can be avoided by careful design,
but if necessary one kludge is to force early/late finalization
by declaring unit X between SysUtils and Forms in the DPR:
uses
Windows,
SysUtils,
X,
Forms,
...
This gives initialization sequence:
SysUtils, X, Controls, Forms, ...
with finalization being the reverse.
Alternatively consider using the AddTerminateProc mechanism used
by the ComServ unit.
SUMMARY
-------
- MainForm.Close calls CloseQuery which gives each MDI child OnCloseQuery,
followed by MainForm.OnClose(caHide) and then Application.Terminate,
which performs PostQuitMessage(0), asking TApplication.Run to finish.
- Windows are destroyed when Forms finalization calls DoneApplication,
which calls TApplication.DestroyComponents. TApplication itself is
destroyed later when Controls finalization calls DoneControls.
- Since windows/components are destroyed AFTER most units are finalized
do not use destructors to perform activity reliant on resources freed
by finalization. Use destructors only to free local resources.
- For forms use CloseQuery instead, but be aware that Application.Terminate
(and Halt) bypass it and go straight to finalization.
- Avoid unit interdependence in initialization/finalization sections.
- There is no point having try/finally around Application.Run.
HTH.
Just my observations - corrections welcome.
I've been able to use those sections to create non-visual objects when my
application starts.
What exactly are you trying to do?