I have been playing a bit with this and basically I considered that every unit needs to be initialized before a proper choice can be presented.
Now, IF the compiler supported something alike AddExitProc(for ExitProc entry) for InitProc, i.e. AddInitProc (which comes after all unit initializations) You could switch at runtime by adding an initproc even before the main program executes.
A version looks like this, must be the first unit - awaiting a system.pas patch I have made:
unit AddInitProcUnit;
{$mode delphi}
interface
procedure AddInitProc(Proc: TProcedure);
implementation
type
PInitProcInfo = ^TInitProcInfo;
TInitProcInfo = record
Proc: TProcedure;
Next: PInitProcInfo;
end;
{$push}{$J+}
const
InitProcList: PInitProcInfo = nil;
{$pop}
procedure DoInitProcs; forward;
procedure AddInitProc(Proc: TProcedure);
var
Info: PInitProcInfo;
OldProc: TProcedure;
begin
{ If there is an existing InitProc that is not our own dispatcher,
add it to the list first so it will be called as part of the chain.}
if (InitProc <> nil) and (InitProc <> @DoInitProcs) then
begin
{ capture the existing routine }
OldProc := InitProc;
New(Info);
Info^.Proc := OldProc;
{ existing proc becomes new head (LIFO) }
Info^.Next := InitProcList;
InitProcList := Info;
end;
{ Now add the new procedure (the one passed as parameter) }
New(Info);
Info^.Proc := Proc;
Info^.Next := InitProcList;
InitProcList := Info;
{ Redirect global InitProc to our dispatcher (if not already) }
if InitProc <> @DoInitProcs then
InitProc := @DoInitProcs;
end;
procedure DoInitProcs;
var
Info, NextInfo: PInitProcInfo;
begin
Info := InitProcList;
{ prevent re‑entry }
InitProcList := nil;
while Info <> nil do
begin
NextInfo := Info^.Next;
{ execute the "stored" procedure }
Info^.Proc;
{ executed once, dispose }
Dispose(Info);
Info := NextInfo;
end;
{ After all procs have run, clear the dispatcher }
InitProc := nil;
end;
{$ifopt D+}
procedure teststart;
begin
writeln('executed after all other units are initialized');
end;
initialization
AddInitProc(@teststart);
{$endif D+}
finalization
// Clean up any remaining init procs if DoInitProcs never ran
if InitProcList <> nil then
DoInitProcs;
end.
This code works, but without the compiler patch, it needs to be called from a unit, not the main program (which works if system.pas is patched).
I hope you see the relevance, otherwise will add full example.