Delphi Pascal-使用SetFilePointerEx和GetFileSizeEx,在作为文件读取时获得物理介质的精确大小
我不知道如何使用RTL中没有的API。我一直在使用SetFilePointer和GetFileSize将物理磁盘读入缓冲区并将其转储到文件中,类似这样的东西在循环中完成2GB以下闪存卡的工作:Delphi Pascal-使用SetFilePointerEx和GetFileSizeEx,在作为文件读取时获得物理介质的精确大小,delphi,api,delphi-2009,pascal,Delphi,Api,Delphi 2009,Pascal,我不知道如何使用RTL中没有的API。我一直在使用SetFilePointer和GetFileSize将物理磁盘读入缓冲区并将其转储到文件中,类似这样的东西在循环中完成2GB以下闪存卡的工作: SetFilePointer(PD,0,nil,FILE_BEGIN); SetLength(Buffer,512); ReadFile(PD,Buffer[0],512,BytesReturned,nil); 然而,GetFileSize和SetFilePointer都有2GB的限制。我完全不知道如何
SetFilePointer(PD,0,nil,FILE_BEGIN);
SetLength(Buffer,512);
ReadFile(PD,Buffer[0],512,BytesReturned,nil);
然而,GetFileSize和SetFilePointer都有2GB的限制。我完全不知道如何删除外部API,我已经查看了RTL和谷歌搜索了许多示例,但没有找到正确的答案
我试过这个
function GetFileSizeEx(hFile: THandle; lpFileSizeHigh: Pointer): DWORD;
external 'kernel32';
正如我所说的
function GetFileSizeEx(hFile: THandle; var FileSize: Int64): DWORD;
stdcall; external 'kernel32';
但该函数返回0,即使我使用的是有效的磁盘句柄,我已经确认并转储了使用旧API的数据
我使用SetFilePointer每512字节跳转一次,并使用ReadFile写入缓冲区,相反,当我使用WriteFile向磁盘写入初始程序加载程序代码或其他内容时,我可以使用它进行设置。我需要能够设置超过2gb的文件指针远远超过
有人能帮我进行外部声明并调用GetFileSizeEx和SetFilePointerEx,这样我就可以修改我的旧代码来处理4到32gb的闪存卡了。我建议你看看这个和他的单元,它应该给你足够的指针来获得文件大小 编辑1鉴于对问题的编辑,现在这个答案看起来相当愚蠢 编辑2现在这个答案已经被接受,在对jachguate的答案进行了一长串评论之后,我觉得有义务总结所学内容
- 及 没有2GB 限制,它们可以用于文件 基本上任意大小的
- 及 很多 更容易使用,因为它们可以工作 直接使用64位数量和 有更简单的错误条件 信号
- 事实上,OP不需要这样做 计算他的磁盘的大小。自从 这篇报道读了整个故事 磁盘的内容大小不正确 需要。所需要的只是 按顺序阅读内容,直到 一无所有
- 其实 / 不支持设备的句柄 (如物理磁盘或卷)如 是OP要求的而且, / 无法搜索到该设备的末尾 把手
- 为了获得一个 磁盘、卷或分区 过关 控制代码至
function GetFileSizeEx(hFile: THandle; var lpFileSize: Int64): BOOL;
stdcall; external 'kernel32.dll';
function SetFilePointerEx(hFile: THandle; liDistanceToMove: Int64;
lpNewFilePointer: PInt64; dwMoveMethod: DWORD): BOOL;
stdcall; external 'kernel32.dll';
获取这些API导入的一个简单方法是通过优秀的。例程需要一个指向数据类型的指针,文档中说:
如果编译器内置了对64位整数的支持,请使用QuadPart成员存储64位整数
幸运的是,Delphi内置了对64位整数的支持,所以请使用它:
var
DriveSize: LongWord;
begin
GetFilePointerSizeEx(PD, @DriveSize);
end;
另一方面,需要liDistanceToMove、lpNewFilePointer的参数,这两个参数都是64位整数。我的理解是,它需要有符号整数,但如果我对文档理解错误,则您有用于无符号64位整数的UInt64数据类型。替代编码
首先,你的方法是错误的,因为你的方法错误,你在Windows处理以文件形式打开的磁盘驱动器时遇到了一些棘手的问题。在伪代码中,您的方法似乎是:
Size = GetFileSize;
for i=0 to (Size / 512) do
begin
Seek(i * 512);
ReadBlock;
WriteBlockToFile;
end;
这在功能上是正确的,但是有一种更简单的方法可以做到这一点,而不需要实际获取SizeOfDisk,也不需要查找。当从文件(或流)中读取内容时,“指针”会随着您刚读取的数据量自动移动,因此您可以跳过“搜索”。用于从文件读取数据的所有函数都返回实际读取的数据量:您可以使用该函数来知道何时到达文件末尾,而不知道要开始读取的文件的大小
下面介绍了如何使用Delphi的TFileStream将物理磁盘读取到文件中,而不需要对磁盘设备有太多了解:
var DiskStream, DestinationStream:TFileStream;
Buff:array[0..512-1] of Byte;
BuffRead:Integer;
begin
// Open the disk for reading
DiskStream := TFileStream.Create('\\.\PhysicalDrive0', fmOpenRead);
try
// Create the file
DestinationStream := TFileStream.Create('D:\Something.IMG', fmCreate);
try
// Read & write in a loop; This is where all the work's done:
BuffRead := DiskStream.Read(Buff, SizeOf(Buff));
while BuffRead > 0 do
begin
DestinationStream.Write(Buff, BuffRead);
BuffRead := DiskStream.Read(Buff, SizeOf(Buff));
end;
finally DestinationStream.Free;
end;
finally DiskStream.Free;
end;
end;
显然,您可以通过另一种方式执行类似的操作,从文件读取数据并写入磁盘。在编写代码之前,我实际上尝试了按您的方式执行(获取文件大小等),并立即遇到了问题!显然Windows不知道“文件”的确切大小,除非您从中读取
作为文件打开的磁盘出现问题
在我的所有测试中,我使用了以下简单代码作为基础:
var F: TFileStream;
begin
F := TFileStream.Create('\\.\PhysicalDrive0', fmOpenRead);
try
// Test code goes here...
finally F.Free;
end;
end;
第一件(显而易见的)尝试是:
ShowMessage(IntToStr(DiskStream.Size));
这是失败的。在依赖于调用FileSeek的TFileStream实现中,FileSeek无法处理大于2Gb的文件。因此,我使用以下代码尝试了GetFileSize:
var RetSize, UpperWord:DWORD;
RetSize := GetFileSize(F.Handle, @UpperWord);
ShowMessage(IntToStr(UpperWord) + ' / ' + IntToStr(RetSize));
var RetPos, UpperWord:DWORD;
UpperWord := 0;
RetPos := SetFilePos(F.Handle, 0, @UpperWord, FILE_END);
ShowMessage(IntToStr(UpperWord) + ' / ' + IntToStr(RetPos));
这也失败了,即使是那些它应该完全能够返回文件大小为64位数字!接下来,我尝试使用SetFilePointer API,因为它还应该处理64位数字。我想我应该简单地查找文件的末尾,并查看结果,使用以下代码:
var RetSize, UpperWord:DWORD;
RetSize := GetFileSize(F.Handle, @UpperWord);
ShowMessage(IntToStr(UpperWord) + ' / ' + IntToStr(RetSize));
var RetPos, UpperWord:DWORD;
UpperWord := 0;
RetPos := SetFilePos(F.Handle, 0, @UpperWord, FILE_END);
ShowMessage(IntToStr(UpperWord) + ' / ' + IntToStr(RetPos));
这个代码也失败了!现在我在想,为什么第一个代码能工作?显然,逐块阅读效果很好,Windows完全知道何时停止阅读!!所以我认为64位文件处理例程的实现可能有问题,让我们尝试以小增量结束文件;当我们得到一个错误查找时,我们知道我们到达了终点,我们将停止:
var PrevUpWord, PrevPos: DWORD;
UpWord, Pos: DWORD;
UpWord := 0;
Pos := SetFilePointer(F.Handle, 1024, @UpWord, FILE_CURRENT); // Advance the pointer 512 bytes from it's current position
while (UpWord <> PrevUpWord) or (Pos <> PrevPos) do
begin
PrevUpWord := UpWord;
PrevPos := Pos;
UpWord := 0;
Pos := SetFilePointer(F.Handle, 1024, @UpWord, FILE_CURRENT);
end;
完整地说,以下两个例程可用于使用Int64设置和获取文件指针以进行定位:
function Hacky_SetStreamPos(F: TFileStream; Pos: Int64):Int64;
var aPos:Int64;
DWA:packed array[0..1] of DWORD absolute aPos;
const INVALID_SET_FILE_POINTER = $FFFFFFFF;
begin
aPos := Pos;
DWA[0] := SetFilePointer(F.Handle, DWA[0], @DWA[1], FILE_BEGIN);
if (DWA[0] = INVALID_SET_FILE_POINTER) and (GetLastError <> NO_ERROR) then
RaiseLastOSError;
Result := aPos;
end;
function Hacky_GetStreamPos(F: TFileStream): Int64;
var Pos:Int64;
DWA:packed array[0..1] of DWORD absolute Pos;
begin
Pos := 0;
DWA[0] := SetFilePointer(F.Handle, 0, @DWA[1], FILE_CURRENT);
Result := Pos;
end;
函数Hacky_SetStreamPos(F:TFileStream;Pos:Int64):Int64;
var-aPos:Int64;
DWA:DWORD绝对APO的压缩数组[0..1];
常量无效\u集\u文件\u指针=$FFFFFFFF;
开始
aPos:=Pos;
DWA[0]:=SetFilePointer(F.Handle,DWA[0],@DWA[1],FILE_BEGIN);
如果(DWA[0]=无效的设置文件指针)和(GetLastError无错误),则
赖斯·塞罗;
结果:=载脂蛋白;
结束;
函数Hacky_GetStreamPos(F:TFileStream):Int64;
var-Pos:Int64;
D
// Ok! Save the last known good position
KnownGoodPosition := KnownGoodPosition + Step;
TDISK_GEOMETRY = record
Cylinders : Int64; //LargeInteger
MediaType : DWORD; //MEDIA_TYPE
TracksPerCylinder: DWORD ;
SectorsPerTrack: DWORD ;
BytesPerSector : DWORD ;
end;
TDISK_GEOMETRY_EX = record
Geometry: TDISK_GEOMETRY ;
DiskSize: Int64; //LARGE_INTEGER ;
Data : array[1..1000] of byte; // unknown length
end;
function get_disk_size(handle: thandle): int64;
var
BytesReturned: DWORD;
DISK_GEOMETRY_EX : TDISK_GEOMETRY_EX;
begin
result := 0;
if DeviceIOControl(handle,IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
nil,0,@DISK_GEOMETRY_EX, sizeof(TDISK_GEOMETRY_EX),BytesReturned,nil)
then result := DISK_GEOMETRY_EX.DiskSize;
end;