Can I determine the order in which my units have been initialized?

后端 未结 5 2060
长情又很酷
长情又很酷 2020-12-10 03:37

I am hunting a bug which might be connected to unit initialization order. Is there a way to see which initialization section was executed when? I need to know t

相关标签:
5条回答
  • 2020-12-10 04:16

    You can set breakpoints on all initialization sections that don't break but write a message to the debugger log. It will give you the same list as adding OutputDebugString('...') calls but without having to modify the source code of all units.

    0 讨论(0)
  • 2020-12-10 04:28

    How about adding

    OutputDebugString('In MyUnit initialization'); 
    

    to the initialization sections?

    0 讨论(0)
  • 2020-12-10 04:30

    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.

    0 讨论(0)
  • 2020-12-10 04:40

    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.

    0 讨论(0)
  • 2020-12-10 04:41

    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 var
    
    procedure 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:

    Variants
    VarUtils
    Windows
    Types
    SysInit
    System
    SysConst
    SysUtils
    Character
    RTLConsts
    Math
    StrUtils
    ImageHlp
    MainUnit
    JwaWinNetWk
    JwaWinType
    JwaWinNT
    JwaWinDLLNames
    JwaWinError
    StdCtrls
    Dwmapi
    UxTheme
    SyncObjs
    Classes
    ActiveX
    Messages
    TypInfo
    TimeSpan
    CommCtrl
    Themes
    Controls
    Forms
    StdActns
    ComCtrls
    CommDlg
    ShlObj
    StructuredQueryCondition
    PropSys
    ObjectArray
    UrlMon
    WinInet
    RegStr
    ShellAPI
    ComStrs
    Consts
    Printers
    Graphics
    Registry
    IniFiles
    IOUtils
    Masks
    DateUtils
    Wincodec
    WinSpool
    ActnList
    Menus
    ImgList
    Contnrs
    GraphUtil
    ZLib
    ListActns
    ExtCtrls
    Dialogs
    HelpIntfs
    MultiMon
    Dlgs
    WideStrUtils
    ToolWin
    RichEdit
    Clipbrd
    FlatSB
    Imm
    TpcShrd
    

    /EDIT2: And here a version for D2009 (requires JclDebug):

    unit MainUnit;
    
    interface
    
    uses
      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.System
    0003: Windows.Windows
    0011: SysUtils.SysUtils
    0012: VarUtils.VarUtils
    0013: Variants.Variants
    0014: TypInfo.TypInfo
    0016: Classes.Classes
    0017: IniFiles.IniFiles
    0018: Registry.Registry
    0020: Graphics.Graphics
    0023: SyncObjs.SyncObjs
    0024: UxTheme.UxTheme
    0025: MultiMon.MultiMon
    0027: ActnList.ActnList
    0028: DwmApi.DwmApi
    0029: Controls.Controls
    0030: Themes.Themes
    0032: Menus.Menus
    0033: HelpIntfs.HelpIntfs
    0034: FlatSB.FlatSB
    0036: Printers.Printers
    0047: GraphUtil.GraphUtil
    0048: ExtCtrls.ExtCtrls
    0051: ComCtrls.ComCtrls
    0054: Dialogs.Dialogs
    0055: Clipbrd.Clipbrd
    0057: Forms.Forms
    0058: JclResources.JclResources
    0059: JclBase.JclBase
    0061: JclWin32.JclWin32
    0063: ComObj.ComObj
    0064: AnsiStrings.AnsiStrings
    0065: JclLogic.JclLogic
    0066: JclStringConversions.JclStringConversions
    0067: JclCharsets.JclCharsets
    0068: Jcl8087.Jcl8087
    0073: JclIniFiles.JclIniFiles
    0074: JclSysInfo.JclSysInfo
    0075: JclUnicode.JclUnicode
    0076: JclWideStrings.JclWideStrings
    0077: JclRegistry.JclRegistry
    0078: JclSynch.JclSynch
    0079: JclMath.JclMath
    0080: JclStreams.JclStreams
    0081: JclAnsiStrings.JclAnsiStrings
    0082: JclStrings.JclStrings
    0083: JclShell.JclShell
    0084: JclSecurity.JclSecurity
    0085: JclDateTime.JclDateTime
    0086: JclFileUtils.JclFileUtils
    0087: JclConsole.JclConsole
    0088: JclSysUtils.JclSysUtils
    0089: JclUnitVersioning.JclUnitVersioning
    0090: JclPeImage.JclPeImage
    0091: JclTD32.JclTD32
    0092: JclHookExcept.JclHookExcept
    0093: JclDebug.JclDebug
    0094: MainUnit.MainUnit
    
    0 讨论(0)
提交回复
热议问题