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
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. –