i have a unit in Delphi that (has the option) to provide a single global object:
var
InternalThemeParkServices: TThemeParkServices;
function ThemeParkServices: TThemeParkServices;
begin
if InternalThemeParkServices= nil then
InternalThemeParkServices := TThemeParkServices.Create();
Result := InternalThemeParkServices ;
end;
...
initialization
finalization
FreeAndNil(InternalThemeServices);
end.
And i destroy myself during process shutdown.
Note: Another code variant is:
var InternalThemeParkServices: IThemeParkServices; function ThemeParkServices: TThemeParkServices; begin if InternalThemeParkServices= nil then InternalThemeParkServices := TThemeParkServices.Create(); Result := InternalThemeParkServices ; end;
Where the interface variable is implicitly destroyed when it's reference count goes to zero during program shutdown
When my object is no longer used (i.e. during its destructor
), i call call various WinAPI functions.
The problem is that if someone uses my class from a DLL (something which i cannot control), then anything being called during:
finalization
is the Delphi moral equivalent of DLL_PROCESS_DETACH
. There are all kinds of things i should not be doing during DLL_PROCESS_DETACH
when the process is terminating (e.g. CoCreateInstance
).
i know Embarcadero uses:
initialization
if not IsLibrary then
begin
...
Which i perhaps i could adapt, changing my code from:
var
InternalThemeParkServices: IThemeParkServices;
(using implicit cleanup), to:
var
InternalThemeParkServices: IThemeParkServices;
...
finalization
if IsLibrary then
Pointer(InternalThemeParkServices) := nil; //defeat reference counting and let the object leak
end;
and let it leak.
But is this the best resolution? i assume it means that if the dll running my code is unloaded (but not during process shutdown) that i'll leak memory. If the dll is attached and detached, i'll leak each time.
What i really want is Delphi to run its finalization
blocks before ExitProcess
/DllMain(DLL_PROCESS_DETACH)
. Is this possible?
@pa deciphered the Delphi application shutdown scheme:
The hierarchy of shutdown is as follows
Application.Terminate() performs some unidentified housekeeping of application calls Halt() Halt() calls ExitProc if set alerts the user in case of runtime error get rid of PackageLoad call contexts that might be pending finalize all units clear all exception handlers call ExitprocessProc if set and finally, call ExitProcess() from 'kernel32.dll' ExitProcess() unloads all DLLs uses TerminateProcess() to kill the process
With DLLs being unloaded after a call to ExitProcess
- because Windows is the one who does it.
To tell if you're being called during DLL_PROCESS_DETACH after ExitProcess has been called, you can write initialization code for your library so that your code is executed when FreeLibrary
is called from the main program. The 'lpReserved' parameter will be '1' if ExitProcess
have already been called, '0' otherwise:
..
var
SaveDllProcEx: TDllProcEx;
procedure DllMainEx(Reason: Integer; Reserved: Integer);
begin
if (Reason = DLL_PROCESS_DETACH) and (Reserved = 0) then
// Main app is still running, cleanup.
if Assigned(SaveDllProcEx) then
SaveDllProcEx(Reason, Reserved);
end;
initialization
if IsLibrary then begin
SaveDllProcEx := DllProcEx;
DllProcEx := @DllMainEx;
end;
From DllMain entry point:
The lpReserved parameter indicates whether the DLL is being unloaded as a result of a FreeLibrary call, a failure to load, or process termination.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With