2010-11-02 8 views
10

Estoy buscando un error que podría estar conectado al orden de inicialización de la unidad. ¿Hay alguna manera de ver qué sección initialization se ejecutó cuando? Necesito saber el orden. Esto es durante la depuración, así que tengo toda la potencia del Delphi IDE, en mi caso Delphi 2009.¿Puedo determinar el orden en que se han inicializado mis unidades?

Podría establecer puntos de interrupción, pero esto es bastante tedioso cuando tengo muchas unidades.

¿Tiene alguna sugerencia?

+1

Relacionados: Si utiliza una unidad en la sección de interfaz, sabrá que esa unidad se inicializará ANTES de la unidad que utiliza esa unidad. Cuando se usan unidades en la sección de implementación, este no es el caso. Por lo tanto, generalmente cuando está usando una unidad con un singleton, creada en su sección de inicialización, debe usar esa unidad en la sección de interfaz para asegurarse de que se inicialice antes de su uso. –

Respuesta

6

Para las unidades en la interfaz de lista, las secciones de inicialización de la unidades utilizadas por un cliente se ejecutan en el orden en que las unidades aparecen en cláusula usos del cliente utiliza.

ver Ayuda en pantalla \ Programs and Units \ La inicialización Sección y este artículo: Understanding Delphi Unit initialization order

ICARUS calcula el orden tiempo de ejecución de inicialización por su Usos Informe:

esta sección se enumeran el orden en que las secciones de inicialización se ejecutan en tiempo de ejecución.

+0

La parte que lo dificulta es cuando Unit1 usa muchas otras Unidades que pueden usar aún más Unidades. – Remko

+0

@Remko Exactamente. –

+0

@Heinrich: Pruebe [ICARUS] (http://www.peganza.com/products_icarus.htm) para el cálculo del orden de inicialización del tiempo de ejecución. – splash

0

Cómo sobre la adición

OutputDebugString('In MyUnit initialization'); 

a las secciones de inicialización?

+0

Lamentablemente no puedo cambiar todas las unidades. Muchos de ellos no están bajo mi control. –

3

Puede consultar la unidad System y SysInit y buscar el procedimiento InitUnits. Aquí puede ver que cada módulo compilado con Delphi tiene una lista de punteros de inicialización y finalización de unidades. Usar esos más un archivo de mapa puede darle el orden de inicialización exacto, pero tomará algún hacker de puntero.

+0

+1 y no olvide habilitar la depuración de dcu, de lo contrario no podrá establecer BP en InitUnits. – Remko

+0

Por cierto, parece que InitContext.Module^.TypeInfo^.UnitNames contiene una matriz de cadenas con los nombres de unidades. Si lo lanzo: PAnsiChar (InitContext.Module^.TypeInfo^.UnitNames) el resultado es (ejemplo): – 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'SyncObjs '# 7'Classes' # 7'ActiveX '# 8'Mensajes' # 7'TypInfo '# 8'TimeSpan'List' # 7'Contnrs '# 9'GraphUtil' # 4'ZLib '# 9'ListActns' # 8'ExtCtrls '# 7' Diálogos, etc. – Remko

0

Puede establecer puntos de interrupción en todas las secciones de inicialización que no se rompen pero que escriben un mensaje en el registro del depurador. Le dará la misma lista que agregar OutputDebugString('...') llamadas, pero sin tener que modificar el código fuente de todas las unidades.

+0

Intento evitar esto ya que en mi opinión es propenso a errores y mucho trabajo. Si tengo posiblemente cientos de unidades dependientes, tendría que establecer un punto de quiebre en todas partes. ¿Y qué pasa si solo tengo un dcu y ningún código fuente? ¿Y qué pasa si me olvido de alguna unidad? Y después del próximo choque de Delphi, todos los puntos de interrupción se han ido. Sería bueno si hubiera una solución más fácil. –

8

Aquí hay un código que acabo de probar en D2010, tenga en cuenta que debe establecer un punto de interrupción en System.InitUnits y obtener la dirección de InitContext var (@InitContext). Luego modifique CtxPtr para tener esta dirección MIENTRAS SIGUE EJECUTANDO. (Tal vez alguien sabe de una manera más inteligente para esto).

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: y aquí es una versión usando JclDebug y un archivo de asignaciones:

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; 

salida en mi caso:

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: Y aquí una versión para D2009 (requiere 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 en mi sistema (Unitname.Unitname es en realidad Unitname.Inicialización):

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 ¡Es impresionante, gracias por su esfuerzo! Es interesante ver el uso de TJclMapParser. Pero, lamentablemente, actualmente estoy obligado a Delphi 2009 y el RTTI fácil no está disponible aquí: -/Pero para todos los que tengan D2010, este podría ser el camino a seguir. –

+0

¿quiere decir InitUnits en la unidad del sistema ?, porque no existe en SysUtils. –

+0

@Mohammed Nasman: Sí, lo corregí. (Si utiliza la versión con JclDebug/Mapfile, no es necesario configurar el punto de interrupción, la dirección se leerá en Mapfile) – Remko

Cuestiones relacionadas