【发布时间】:2012-07-17 19:36:07
【问题描述】:
我正在编写一个小型 PE 文件分析器,我必须读取 PE 文件的内容。我是通过ReadFile函数来实现的,如下图:
function TMainForm.GetPEData(var filename: string) : boolean;
var
hFile: DWORD;
IDH: TImageDosHeader;
INH: TImageNtHeaders;
ISH: TImageSectionHeader;
dwRead: DWORD;
szBuff: array[0..7] of Char;
i: WORD;
PE: TPEFile;
begin
Result := False;
PE := TPeFile.Create;
if PE.LoadFromFile (filename) then
Form2.edEntryPoint.Text := IntToHex(PE.RvaToFileOffset(PE.AddressOfEntryPoint), 8);
SplashScreen.sLabel1.Caption := 'PE File Loaded';
hFile := CreateFile(PChar(filename), GENERIC_READ,
FILE_SHARE_WRITE, nil,
OPEN_EXISTING, 0, 0);
if hFile <> INVALID_HANDLE_VALUE then
begin
SetFilePointer(hFile, 0, nil, FILE_BEGIN);
SplashScreen.sLabel1.Caption := 'Reading DOS File Headers...';
ReadFile(hFile, IDH, 64, dwRead, nil);
if IDH.e_magic = IMAGE_DOS_SIGNATURE then
begin
SetFilePointer(hFile, IDH._lfanew, nil, FILE_BEGIN);
SplashScreen.sLabel1.Caption := 'Reading NT File Headers...';
//Here is where the UI freezes while the file is read...
ReadFile(hFile, INH, 248, dwRead, nil);
if INH.Signature = IMAGE_NT_SIGNATURE then
begin
Form2.edImageBase.Text := IntToHex(INH.OptionalHeader.ImageBase, 8);
Form2.edSizeOfImage.Text := IntToHex(INH.OptionalHeader.SizeOfImage, 8);
Form2.edLinkerVersion.Text := IntToStr(INH.OptionalHeader.MajorLinkerVersion) + '.' +
IntToStr(INH.OptionalHeader.MinorLinkerVersion);
Form2.edFileAlignment.Text := IntToHex(INH.OptionalHeader.FileAlignment, 8);
Form2.edSectionAlignment.Text := IntToHex(INH.OptionalHeader.SectionAlignment, 8);
Form2.edSubSystem.Text := IntToHex(INH.OptionalHeader.Subsystem, 4);
Form2.edEPFilestamp.Text := IntToStr(INH.FileHeader.TimeDateStamp);
Form2.edFileType.Text := GetPEFileType(PE.ImageNtHeaders.Signature);
for i := 0 to INH.FileHeader.NumberOfSections - 1 do
begin
SetFilePointer(hFile, IDH._lfanew + 248 + i * 40, nil, FILE_BEGIN);
ReadFile(hFile, ISH, 40, dwRead, nil);
CopyMemory(@szBuff[0], @ISH.Name[0], 8);
with Form2.sListView1.Items.Add do
begin
Caption := ShortString(szBuff);
SubItems.Add(IntToHex(ISH.VirtualAddress, 8));
SubItems.Add(IntToHex(ISH.Misc.VirtualSize, 8));
SubItems.Add(IntToHex(ISH.PointerToRawData, 8));
SubItems.Add(IntToHex(ISH.SizeOfRawData, 8));
SubItems.Add(IntToHex(ISH.Characteristics, 8));
end;
end;
end;
end;
CloseHandle(hFile);
Result := True;
end;
end;
不好的是,根据文件的大小,我注意到ReadFile 经常会滞后——而且它是同步发生的。与此同时,UI 冻结并且对用户来说看起来非常错误,他们会很想终止它。我考虑过线程,但我只是想看看有没有什么方法可以在异步模式下使用ReadFile。如果没有,我会跳到线程,即使我的代码有很多要修改的地方。
提前谢谢你。
【问题讨论】:
-
只是出于好奇,您在这里重新发明轮子有什么原因吗? 2007 年之前的 Delphi 版本有一个转储 PE 信息的演示(此处没有演示名称,但稍后会发布),JEDI Code Library 有一个完整的单元 (
JclPEImage) 包含即用型为您完成所有这些工作的功能。 (甚至还有一个 PEViewer 演示,它以与DependencyWalker非常相似的方式显示所有内容。)所有代码在非常大的可执行文件(5 MB+)上也能正常运行,没有任何 UI 问题或滞后。 -
嘿,@KenWhite,我对实现没有任何偏好。事实上,我排除了 JEDI,因为一个单元链接到另一个单元等等,这 - 恕我直言 - 使得整个项目比我有一个单一的功能更难维护。我会看一下旧的 Delphi 演示,我想我仍然有 Delphi 7 的光盘。
-
当我进入我的家庭系统时,我会从 D7 中找到演示名称并将其发布在此处的评论中。
JCL比JVCL更好地使用一个单元一个单元,因为它是严格的代码。它确实添加了一些其他内容,但在这种情况下,您将在您的使用子句中添加一个单元名称以包含JclPEImage,并从您的代码中调用简单的函数。这还不错(尽管我同意 JVCL 令人讨厌地相互依赖于它包含的其他单元)。 -
即使是 JCL 与其他 JCL 单元的耦合也相当糟糕。
-
D7 中的演示名称是
ResXPlor,在$(DELPHI)\Demos文件夹中。有一个名为ExeImage的单元,我相信它包含您正在寻找的代码,尽管我没有专门检查它。 @WarrenP,我并没有说这还不错;我说它没有 JVCL那么糟糕。 :-)
标签: multithreading delphi asynchronous readfile