How can i tell if i'm being called during DLL_PROCESS_DETACH after ExitProcess has been called?
delphi, delphi-5, dll, process, winapi
Solution
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.
Problem
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? Bonus Chatter @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.