Delphi Pascal-使用SetFilePointerEx和GetFileSizeEx,在作为文件读取时获得物理介质的精确大小

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的限制。我完全不知道如何

我不知道如何使用RTL中没有的API。我一直在使用SetFilePointer和GetFileSize将物理磁盘读入缓冲区并将其转储到文件中,类似这样的东西在循环中完成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;