我可以确定我的单元初始化的顺序吗?

发布于 2024-09-30 05:32:09 字数 171 浏览 8 评论 0原文

我正在寻找一个可能与单元初始化顺序有关的错误。有没有办法查看哪个初始化部分何时执行?我需要知道顺序。这是在调试期间,所以我拥有 Delphi IDE 的全部功能,在我的例子中是 Delphi 2009。

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

您有什么建议吗?

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 the order. This is during debugging, so I have the full power of the Delphi IDE, in my case Delphi 2009.

I could set breakpoints, but this is rather tedious when having many units.

Do you have any suggestions?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

小红帽 2024-10-07 05:32:09

这是我刚刚在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^.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

/编辑2:这里是 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

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
苏辞 2024-10-07 05:32:09

对于界面使用列表中的单位,
的初始化部分
客户端使用的单元执行于
单位出现的顺序
客户的使用条款。

请参阅在线帮助\程序和单元< /a> \ 初始化部分 和本文:了解 Delphi 单元初始化order

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

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

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.

梦一生花开无言 2024-10-07 05:32:09

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

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.

怕倦 2024-10-07 05:32:09

怎么样?

OutputDebugString('In MyUnit initialization'); 

添加到初始化部分

How about adding

OutputDebugString('In MyUnit initialization'); 

to the initialization sections?

缺⑴份安定 2024-10-07 05:32:09

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文