2010-11-02 55 views
10

我在找一个可能连接到单元初始化顺序的错误。有没有办法看到哪个initialization节被执行?我需要知道订单。这是在调试过程中,所以我拥有Delphi IDE的全部功能,在我的案例中为Delphi 2009。我可以确定我的单位已经初始化的顺序吗?

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

你有什么建议吗?

+1

相关:如果您在界面部分使用单位,您知道该单位将在使用该单位的单位之前初始化。在实现部分使用单元时,情况并非如此。所以通常当你在一个单元中使用一个单元时,在它的初始化部分中创建一个单元,你应该在界面部分使用这个单元来确保它在使用之前被初始化。 – 2010-11-02 10:32:21

回答

6

对于在接口单元使用列表中,由客户端所使用的 单元 初始化部在 ,其中单元出现在 客户端的用途子句的顺序被执行。

看到联机帮助\ Programs and Units \初始化部分和这篇文章:Understanding Delphi Unit initialization order

ICARUS计算运行初始化顺序用途报告

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

+0

难度较大的部分是Unit1使用多个可能使用更多单位的其他单位。 – Remko 2010-11-02 10:49:26

+0

@Remko正确。 – 2010-11-02 11:03:10

+0

@ Heinrich:尝试[ICARUS](http://www.peganza.com/products_icarus.htm)来计算运行时初始化顺序。 – splash 2010-11-02 12:00:45

0

如何添加

OutputDebugString('In MyUnit initialization'); 

到初始化部分?

+0

不幸的是我不能改变所有的单位。他们中的许多人不受我控制。 – 2010-11-02 09:51:40

3

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

+0

+1,并且不要忘记启用调试dcu,否则你不能在InitUnits中设置BP。 – Remko 2010-11-02 10:24:09

+0

顺便说一句,它似乎像InitContext.Module^.TypeInfo^.UnitNames包含一个单位名称的字符串数组。 如果我投它:PAnsiChar(InitContext.Module^.TypeInfo^.UnitNames)结果是(例如): – Remko 2010-11-02 10:30:15

+0

#8'Variants '#8'VarUtils' #7'Windows '#5'Types' #7'SysInit '#6'System' #8'SysConst '#8'SysUtils' #9'Character '#9'RTLConsts' #4'Math '#8'StrUtils' #8'ImageHlp '#8'MainUnit' #$ B” JwaWinNetWk '#$ A'JwaWinType' #8'JwaWinNT '#$ E'JwaWinDLLNames' #$ B'JwaWinError '#8'StdCtrls' #6'Dwmapi '#7'UxTheme' #8'SyncObjs '#7'Classes' #7'ActiveX '#8'Messages' #7'TypInfo '#8'TimeSpan'List' #7'Contnrs '#9'GraphUtil' #4'ZLib '#9'ListActns' #8'ExtCtrls '#7'对话框'等等 – Remko 2010-11-02 10:30:52

0

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

+0

我会尽量避免这种在我看来,这是容易出错和大量的工作。如果我有可能数百个依赖单位,我将不得不在任何地方设置一个中断点。如果我只有一个dcu而没有源代码呢​​?如果我忘记了一些单位呢?而在下一次Delphi崩溃之后,所有的断点都消失了。如果有一个更简单的解决方案,这将是很好的。 – 2010-11-02 10:17:26

8

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

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; 

/编辑:这里是使用JclDebug和映射文件版本:

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; 

输出在我的情况:

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. 

Ou在我的系统上输入(Unitname.Unitname实际上是Unitname。初始化):

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

+1这非常令人印象深刻,感谢您的努力!看到使用TJclMapParser很有趣。但不幸的是,我目前必须使用Delphi 2009,而简单的RTTI在此处不可用: - /但对于使用D2010的人来说,这可能是一条可行的路。 – 2010-11-02 13:50:12

+0

是否指系统单元中的InitUnits ?,因为它不存在于SysUtils中。 – 2010-11-02 17:41:21

+0

@Mohammed Nasman:是的,我纠正了它。 (如果您使用JclDebug /映射文件的版本就没有必要设置断点,该地址将被映射文件的读出) – Remko 2010-11-02 18:17:44

相关问题