我可以确定我的单位已经初始化的顺序吗?

我正在寻找可能连接到单元初始化顺序的错误。 有没有办法看到哪个initialization部分被执行? 我需要知道顺序。 这是在debugging期间,所以我拥有Delphi IDE的全部function,在我的案例Delphi 2009中。

我可以设置断点,但有很多单位时,这是相当乏味的。

你有什么build议吗?

对于接口使用列表中的单元,客户使用的单元的初始化部分按照单元出现在客户使用条款中的顺序执行。

请参阅联机帮助\ 程序和单元 \初始化部分和本文: 了解Delphi单元初始化顺序

ICARUS计算其使用报告运行时初始化顺序

本节列出了在运行时执行初始化部分的顺序。

这里是我刚刚在D2010中测试的一些代码,请注意,您需要在System.InitUnits中设置一个断点,并获取InitContext var(@InitContext)的地址。 然后修改CtxPtr有这个地址,但仍然运行。 (也许有人知道更聪明的方式)。

 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^.modulee^.TypeInfo^.UnitNames; for i := 0 to CtxPtr^.modulee^.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; 

/编辑:这里是一个使用JclDebug和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^.modulee^.TypeInfo^.UnitNames; for i := 0 to CtxPtr^.modulee^.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; 

输出在我的情况下:

 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:这里是D2009的一个版本(需要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. 

我的系统上的输出(Unitname.Unitname实际上是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 

您可以检出单元System和SysInit并查找InitUnits过程。 在这里你可以看到用Delphi编译的每个模块都有一个单元初始化和终结指针列表。 使用这些加上一个映射文件可能会给你确切的初始化顺序,但它会需要一些指针hackery。

如何添加

 OutputDebugString('In MyUnit initialization'); 

到初始化部分?

您可以在所有不会中断的初始化部分设置断点,但可以将消息写入调试器日志。 它会给你与添加OutputDebugString('...')调用相同的列表,但不必修改所有单元的源代码。