Reputation: 256621
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.
Upvotes: 4
Views: 1281
Reputation: 54792
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.
Upvotes: 6
Reputation: 612844
What I really want is Delphi to run its
finalization
blocks beforeDllMain(DLL_PROCESS_DETACH)
. Is this possible?
No it is not possible.
If you need to perform shutdown actions that cannot be done during DllMain(DLL_PROCESS_DETACH)
then you will need to add an exported function to your DLL that peforms the finalization. You should then require your DLL's clients to call this function before unloading the DLL. This is the same pattern as CoInitialize
/CoUninitialize
.
Upvotes: 2