Can I determine the order in which my units have been initialized?
Here is some code I just tested in D2010, note that you need to set a Breakpoint in System.InitUnits and get the address of InitContext var (@InitContext). Then modify CtxPtr to have this address WHILE STILL RUNNING. (Maybe someone knows a smarter way for this).
procedure TForm3.Button2Click(Sender: TObject);var sl: TStringList; ps: PShortString; CtxPtr: PInitContext;begin // Get the address by setting a BP in SysUtils.InitUnits (or map file?) CtxPtr := PInitContext($4C3AE8); sl := TStringList.Create; try ps := CtxPtr^.Module^.TypeInfo^.UnitNames; for i := 0 to CtxPtr^.Module^.TypeInfo^.UnitCount - 1 do begin sl.Add(ps^); // Move to next unit DWORD(ps) := DWORD(ps) + Length(ps^) + 1; end; Memo1.Lines.Assign(sl); finally sl.Free; end;end;
/EDIT: and here is a version using JclDebug and a mapfile:
type TForm3 = class(TForm) ... private { Private declarations } var Segments: array of DWORD; procedure PublicsByValue(Sender: TObject; const Address: TJclMapAddress; const Name: string); procedure MapSegment(Sender: TObject; const Address: TJclMapAddress; Len: Integer; const GroupName, UnitName: string); procedure MapClassTable(Sender: TObject; const Address: TJclMapAddress; Len: Integer; const SectionName, GroupName: string); public { Public declarations } end;var Form3: TForm3; CtxPtr: PInitContext = nil; // Global varprocedure TForm3.MapClassTable(Sender: TObject; const Address: TJclMapAddress; Len: Integer; const SectionName, GroupName: string);begin SetLength(Segments, Length(Segments) + 1); SegMents[Address.Segment-1] := Address.Offset;end;procedure TForm3.PublicsByValue(Sender: TObject; const Address: TJclMapAddress; const Name: string);const InitContextStr = 'System.InitContext';begin if RightStr(Name, Length(InitContextStr)) = InitContextStr then begin CtxPtr := PInitContext(Segments[Address.Segment-1] + Address.Offset); end;end;procedure TForm3.Button2Click(Sender: TObject);var MapParser: TJclMapParser; MapFile: String; sl: TStringList; ps: PShortString; i: Integer;begin MapFile := ChangeFileExt(Application.ExeName, '.map'); MapParser := TJclMapParser.Create(MapFile); try MapParser.OnPublicsByValue := PublicsByValue; MapParser.OnClassTable := MapClassTable; MapParser.Parse; finally MapParser.Free; end; if CtxPtr = nil then Exit; sl := TStringList.Create; try ps := CtxPtr^.Module^.TypeInfo^.UnitNames; for i := 0 to CtxPtr^.Module^.TypeInfo^.UnitCount - 1 do begin sl.Add(ps^); // Move to next unit DWORD(ps) := DWORD(ps) + Length(ps^) + 1; end; Memo1.Lines.Assign(sl); finally sl.Free; end;end;
Output in my case:
VariantsVarUtilsWindowsTypesSysInitSystemSysConstSysUtilsCharacterRTLConstsMathStrUtilsImageHlpMainUnitJwaWinNetWkJwaWinTypeJwaWinNTJwaWinDLLNamesJwaWinErrorStdCtrlsDwmapiUxThemeSyncObjsClassesActiveXMessagesTypInfoTimeSpanCommCtrlThemesControlsFormsStdActnsComCtrlsCommDlgShlObjStructuredQueryConditionPropSysObjectArrayUrlMonWinInetRegStrShellAPIComStrsConstsPrintersGraphicsRegistryIniFilesIOUtilsMasksDateUtilsWincodecWinSpoolActnListMenusImgListContnrsGraphUtilZLibListActnsExtCtrlsDialogsHelpIntfsMultiMonDlgsWideStrUtilsToolWinRichEditClipbrdFlatSBImmTpcShrd
/EDIT2: And here a version for D2009 (requires JclDebug):
unit MainUnit;interfaceuses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StrUtils, JclDebug, StdCtrls;type TForm1 = class(TForm) Button1: TButton; Memo1: TMemo; procedure Button1Click(Sender: TObject); private { Private declarations } var Segments: array of DWORD; procedure PublicsByValue(Sender: TObject; const Address: TJclMapAddress; const Name: string); procedure MapClassTable(Sender: TObject; const Address: TJclMapAddress; Len: Integer; const SectionName, GroupName: string); public { Public declarations } end;var Form1: TForm1; CtxPtr: PInitContext = nil; // Global var Symbols: TStringList;implementation{$R *.dfm}procedure TForm1.Button1Click(Sender: TObject);var MapParser: TJclMapParser; MapFile: String; sl: TStringList; ps: PShortString; i: Integer; s: String; Idx: Integer;begin MapFile := ChangeFileExt(Application.ExeName, '.map'); MapParser := TJclMapParser.Create(MapFile); try MapParser.OnPublicsByValue := PublicsByValue; MapParser.OnClassTable := MapClassTable; Memo1.Lines.BeginUpdate; MapParser.Parse; Memo1.Lines.EndUpdate; finally MapParser.Free; end; if CtxPtr = nil then Exit; sl := TStringList.Create; try for i := 0 to CtxPtr^.InitTable.UnitCount-1 do begin if Assigned(CtxPtr^.InitTable.UnitInfo^[i].Init) then begin s := Format('$%.8x', [DWORD(CtxPtr^.InitTable.UnitInfo^[i].Init)]); Idx := Symbols.IndexOfObject(TObject(CtxPtr^.InitTable.UnitInfo^[i].Init)); if Idx > -1 then begin Memo1.Lines.Add(Format('%.4d: %s', [i, Symbols[Idx]])); end; end; end; finally sl.Free; end;end;procedure TForm1.MapClassTable(Sender: TObject; const Address: TJclMapAddress; Len: Integer; const SectionName, GroupName: string);begin SetLength(Segments, Length(Segments) + 1); SegMents[Address.Segment-1] := Address.Offset;end;procedure TForm1.PublicsByValue(Sender: TObject; const Address: TJclMapAddress; const Name: string);const InitContextStr = 'System.InitContext';begin if RightStr(Name, Length(InitContextStr)) = InitContextStr then begin CtxPtr := PInitContext(Segments[Address.Segment-1] + Address.Offset); end else begin Symbols.AddObject(Name, TObject(Segments[Address.Segment-1] + Address.Offset)); end;end;initialization Symbols := TStringList.Create; Symbols.Sorted := True; Symbols.Duplicates := dupIgnore;finalization FreeAndNil(Symbols);end.
Output on my system (Unitname.Unitname is actually Unitname.Initialization):
0001: System.System0003: Windows.Windows0011: SysUtils.SysUtils0012: VarUtils.VarUtils0013: Variants.Variants0014: TypInfo.TypInfo0016: Classes.Classes0017: IniFiles.IniFiles0018: Registry.Registry0020: Graphics.Graphics0023: SyncObjs.SyncObjs0024: UxTheme.UxTheme0025: MultiMon.MultiMon0027: ActnList.ActnList0028: DwmApi.DwmApi0029: Controls.Controls0030: Themes.Themes0032: Menus.Menus0033: HelpIntfs.HelpIntfs0034: FlatSB.FlatSB0036: Printers.Printers0047: GraphUtil.GraphUtil0048: ExtCtrls.ExtCtrls0051: ComCtrls.ComCtrls0054: Dialogs.Dialogs0055: Clipbrd.Clipbrd0057: Forms.Forms0058: JclResources.JclResources0059: JclBase.JclBase0061: JclWin32.JclWin320063: ComObj.ComObj0064: AnsiStrings.AnsiStrings0065: JclLogic.JclLogic0066: JclStringConversions.JclStringConversions0067: JclCharsets.JclCharsets0068: Jcl8087.Jcl80870073: JclIniFiles.JclIniFiles0074: JclSysInfo.JclSysInfo0075: JclUnicode.JclUnicode0076: JclWideStrings.JclWideStrings0077: JclRegistry.JclRegistry0078: JclSynch.JclSynch0079: JclMath.JclMath0080: JclStreams.JclStreams0081: JclAnsiStrings.JclAnsiStrings0082: JclStrings.JclStrings0083: JclShell.JclShell0084: JclSecurity.JclSecurity0085: JclDateTime.JclDateTime0086: JclFileUtils.JclFileUtils0087: JclConsole.JclConsole0088: JclSysUtils.JclSysUtils0089: JclUnitVersioning.JclUnitVersioning0090: JclPeImage.JclPeImage0091: JclTD32.JclTD320092: JclHookExcept.JclHookExcept0093: JclDebug.JclDebug0094: MainUnit.MainUnit
For units in the interface uses list, the initialization sections of the units used by a client are executed in the order in which the units appear in the client's uses clause.
see Online Help \ Programs and Units \ The Initialization Section and this article: Understanding Delphi Unit initialization order
ICARUS computes the Runtime initialization order for its Uses Report:
This section lists the order in which the initialization sections are executed at runtime.
You might check out the unit System and SysInit and look for the procedure InitUnits. Here you see that every module compiled with Delphi has a list of units initialization and finalization pointers. Using those plus a map file might give you the exact initialization order, but it will take some pointer hackery.