2010-11-02 21 views
10

Tôi đang tìm kiếm lỗi có thể được kết nối với thứ tự khởi tạo đơn vị. Có cách nào để xem phần initialization nào được thực thi khi nào? Tôi cần phải biết thứ tự. Đây là trong quá trình gỡ lỗi, vì vậy tôi có toàn bộ sức mạnh của IDE Delphi, trong trường hợp của tôi là Delphi 2009.Tôi có thể xác định thứ tự các đơn vị của tôi đã được khởi tạo không?

Tôi có thể đặt điểm ngắt, nhưng điều này khá tẻ nhạt khi có nhiều đơn vị.

Bạn có bất cứ đề xuất nào không?

+1

Liên quan: Nếu bạn sử dụng đơn vị trong phần giao diện, bạn biết rằng đơn vị đó sẽ được khởi chạy TRƯỚC KHI đơn vị sử dụng đơn vị đó. Khi sử dụng các đơn vị trong phần thực hiện, đây không phải là trường hợp. Vì vậy, thường khi bạn đang sử dụng một đơn vị với một singleton trong nó, được tạo ra trong phần khởi tạo của nó, bạn nên sử dụng đơn vị đó trong phần giao diện để đảm bảo rằng nó được khởi tạo trước khi sử dụng. –

Trả lời

6

Đối với đơn vị trong giao diện sử dụng danh sách, các phần khởi tạo của các đơn vị được sử dụng bởi một khách hàng được thực hiện trong thứ tự mà các đơn vị xuất hiện trong khoản sử dụng của khách hàng.

thấy Hỗ trợ trực tuyến \ Programs and Units \ sở khởi Mục và bài viết này: Understanding Delphi Unit initialization order

ICARUS tính thứ tự Runtime khởi cho của nó Sử dụng Báo cáo:

này phần danh sách thứ tự các phần khởi tạo được thực thi khi chạy.

+0

Phần gây khó khăn là khi Unit1 sử dụng một số Đơn vị khác có thể sử dụng nhiều Đơn vị hơn. – Remko

+0

@Remko Chính xác. –

+0

@Heinrich: Thử [ICARUS] (http://www.peganza.com/products_icarus.htm) để tính toán thứ tự khởi tạo thời gian chạy. – splash

0

Làm thế nào về việc thêm

OutputDebugString('In MyUnit initialization'); 

vào các phần khởi tạo?

+0

Rất tiếc, tôi không thể thay đổi tất cả các đơn vị. Nhiều người trong số họ không dưới sự kiểm soát của tôi. –

3

Bạn có thể kiểm tra đơn vị Hệ thống và SysInit và tìm thủ tục InitUnits. Ở đây bạn thấy rằng mỗi mô-đun được biên dịch với Delphi có một danh sách các đơn vị khởi tạo và con trỏ hoàn thiện. Sử dụng những cộng với một tập tin bản đồ có thể cung cấp cho bạn thứ tự khởi tạo chính xác, nhưng nó sẽ mất một số con trỏ hackery.

+0

+1 và đừng quên để kích hoạt gỡ lỗi của DCU khác bạn không thể thiết lập của BP trong InitUnits. – Remko

+0

Btw có vẻ như InitContext.Module^.TypeInfo^.UnitNames chứa một mảng các chuỗi có tên đơn vị. Nếu tôi truyền nó: PAnsiChar (InitContext.Module^.TypeInfo^.UnitNames) kết quả là (ví dụ): – Remko

+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 'Đồng bộ' # 7'Classes ' # 7'ActiveX '# 8'Messages' # 7'TypInfo '# 8'TimeSpan'List' # 7'Contnrs '# 9'GraphUtil' # 4'ZLib '# 9'ListActns' # 8'ExtCtrls '# 7' Dialogs 'và như vậy – Remko

0

Bạn có thể đặt điểm ngắt trên tất cả các phần khởi tạo không bị ngắt nhưng viết thông báo vào nhật ký trình gỡ lỗi. Nó sẽ cung cấp cho bạn danh sách giống như thêm OutputDebugString('...') cuộc gọi nhưng không phải sửa đổi mã nguồn của tất cả các đơn vị.

+0

Tôi cố gắng tránh điều này theo ý kiến ​​của tôi nó là dễ bị lỗi và một rất nhiều công việc. Nếu tôi có thể có hàng trăm đơn vị phụ thuộc, tôi sẽ phải đặt một điểm phá vỡ ở khắp mọi nơi. Và nếu tôi chỉ có một mã nguồn dcu và không có mã nguồn thì sao? Và nếu tôi quên một số đơn vị thì sao? Và sau vụ tai nạn Delphi tiếp theo, tất cả các điểm dừng đều biến mất. Nó sẽ là tốt đẹp nếu có một giải pháp dễ dàng hơn. –

8

Dưới đây là một số mã tôi vừa thử nghiệm trong D2010, lưu ý rằng bạn cần đặt Breakpoint trong System.InitUnits và lấy địa chỉ InitContext var (@InitContext). Sau đó sửa đổi CtxPtr để có địa chỉ này WHILE STILL RUNNING. (Có thể ai đó biết cách thông minh hơn).

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: và đây là một phiên bản sử dụng JclDebug và một 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 trong trường hợp của tôi:

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: Và đây là một phiên bản dành cho D2009 (yêu cầu 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 tput trên hệ thống của tôi (Unitname.Unitname thực sự là Unitname.Khởi tạo):

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 Đó là khá ấn tượng, nhờ nỗ lực của bạn! Thật thú vị khi thấy việc sử dụng TJclMapParser. Nhưng tiếc là tôi hiện đang bị ràng buộc để Delphi 2009 và RTTI dễ dàng không có sẵn ở đây: -/Nhưng đối với tất cả mọi người với D2010 này có thể là con đường để đi. –

+0

bạn có nghĩa là InitUnits trong đơn vị hệ thống ?, bởi vì nó không tồn tại trong SysUtils. –

+0

@Mohammed Nasman: Vâng, tôi đã sửa nó. (Nếu bạn sử dụng phiên bản với JclDebug/Mapfile không cần phải đặt Breakpoint, địa chỉ sẽ được đọc ra khỏi Mapfile) – Remko

Các vấn đề liên quan