Delphi Pascal - 使用 SetFilePointerEx 和 GetFileSizeEx,在读取文件时获取物理媒体的确切大小

发布于 2024-10-09 19:07:19 字数 910 浏览 8 评论 0原文

我不知道如何使用 RTL 之外的任何 API。我一直在使用 SetFilePointer 和 GetFileSize 将物理磁盘读入缓冲区并将其转储到文件中,这样的循环可以完成 2GB 以下闪存卡的工作:

SetFilePointer(PD,0,nil,FILE_BEGIN);
SetLength(Buffer,512);
ReadFile(PD,Buffer[0],512,BytesReturned,nil);

但是 GetFileSize 有 2GB 的限制,SetFilePointer 也是如此。我完全不知道如何删除外部 API,我查看了 RTL 并在 google 上搜索了许多示例,但没有找到正确的答案。

我尝试了这个

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 闪存卡。

I do not know how to use any API that is not in the RTL. I have been using SetFilePointer and GetFileSize to read a Physical Disk into a buffer and dump it to a file, something like this in a loop does the job for flash memory cards under 2GB:

SetFilePointer(PD,0,nil,FILE_BEGIN);
SetLength(Buffer,512);
ReadFile(PD,Buffer[0],512,BytesReturned,nil);

However GetFileSize has a limit at 2GB and so does SetFilePointer. I have absolutley no idea how to delcare an external API, I have looked at the RTL and googled for many examples and have found no correct answer.

I tried this

function GetFileSizeEx(hFile: THandle; lpFileSizeHigh: Pointer): DWORD; 
    external 'kernel32';

and as suggested this

function GetFileSizeEx(hFile: THandle; var FileSize: Int64): DWORD;
    stdcall; external 'kernel32';

But the function returns a 0 even though I am using a valid disk handle which I have confirmed and dumped data from using the older API's.

I am using SetFilePointer to jump every 512 bytes and ReadFile to write into a buffer, in reverse I can use it to set when I am using WriteFile to write Initial Program Loader Code or something else to the disk. I need to be able to set the file pointer beyond 2gb well beyond.

Can someone help me make the external declarations and a call to both GetFileSizeEx and SetFilePointerEx that work so I can modify my older code to work with say 4 to 32gb flash cards.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

咆哮 2024-10-16 19:07:19

我建议您看一下这篇 Primoz Gabrijelcic 博客文章 和他的 < href="http://gp.17slon.com/gp/gphugefile.htm">GpHugeFile 单元应该为您提供足够的指针来获取文件大小。

编辑 1 鉴于对问题的编辑,现在这看起来是一个相当愚蠢的答案。

编辑2 既然这个答案已被接受,在对 jachguate 的答案进行了大量评论之后,我觉得有责任总结一下所学到的内容。

最后,如果您需要使用 GetFileSizeEx< /a> 和 SetFilePointerEx那么它们可以声明如下:

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 导入的一种简单方法是通过优秀的 JEDI API 库

I suggest that you take a look at this Primoz Gabrijelcic blog article and his GpHugeFile unit which should give you enough pointers to get the file size.

Edit 1 This looks rather a daft answer now in light of the edit to the question.

Edit 2 Now that this answer has been accepted, following a long threads of comments to jachguate's answer, I feel it incumbent to summarise what has been learnt.

  • GetFileSize and
    SetFilePointer have no 2GB
    limitation, they can be used on files
    of essentially arbitrary size.

  • GetFileSizeEx and
    SetFilePointerEx are much
    easier to use because they work
    directly with 64 bit quantities and
    have far simpler error condition
    signals.

  • The OP did not in fact need to
    calculate the size of his disk. Since
    the OP was reading the entire
    contents of the disk the size was not
    needed. All that was required was to
    read the contents sequentially until
    there was nothing left.

  • In fact
    GetFileSize/GetFileSizeEx
    do not support handles to devices
    (e.g. a physical disk or volume) as
    was requested by the OP. What's more,
    SetFilePointer/SetFilePointerEx
    cannot seek to the end of such device
    handles.

  • In order to obtain the size of a
    disk, volume or partition, one should
    pass the the
    IOCTL_DISK_GET_LENGTH_INFO
    control code to
    DeviceIoControl.

Finally, should you need to use GetFileSizeEx and SetFilePointerEx then they can be declared as follows:

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';

One easy way to obtain these API imports them is through the excellent JEDI API Library.

戈亓 2024-10-16 19:07:19

GetFileSizeEx 例程需要一个指向LARGE_INTEGER 数据类型和文档说:

如果你的编译器内置了对 64 位整数的支持,请使用 QuadPart 成员来存储 64 位整数。

幸运的是,Delphi 内置了对 64 位整数的支持,所以使用它:

var
  DriveSize: LongWord;
begin
  GetFilePointerSizeEx(PD, @DriveSize);
end;

SetFilePointerEx 需要 liDistanceToMove 的参数, lpNewFilePointer,均为 64 位整数。我的理解是它需要有符号整数,但如果我误解了文档,则您有 Unsingned 64 位整数的 UInt64 数据类型。

The GetFileSizeEx routine expects a pointer to a LARGE_INTEGER data type, and documentation says:

If your compiler has built-in support for 64-bit integers, use the QuadPart member to store the 64-bit integer

Lucky you, Delphi has built-in support for 64 bit integers, so use it:

var
  DriveSize: LongWord;
begin
  GetFilePointerSizeEx(PD, @DriveSize);
end;

SetFilePointerEx, on the other hand, expects parameters for liDistanceToMove, lpNewFilePointer, both 64 bit integers. My understanding is it wants signed integers, but you have the UInt64 data type for Unsingned 64 bit integers if I'm missunderstanding the documentation.

静待花开 2024-10-16 19:07:19

替代编码

自杀,首先你的方法是错误的,并且由于你的错误方法,你在 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));

失败了。在 TFileStream 实现中依赖于调用 FileSeek,而 FileSeek 无法处理大于 2Gb 的文件。因此,我使用以下代码尝试了 GetFileSize:

var RetSize, UpperWord:DWORD;
RetSize := GetFileSize(F.Handle, @UpperWord);
ShowMessage(IntToStr(UpperWord) + ' / ' + IntToStr(RetSize));

这也失败了,即使它应该完全能够以 64 位数字形式返回文件大小!接下来我尝试使用 SetFilePointer API,因为它也应该处理 64 位数字。我想我只需使用以下代码查找文件末尾并查看结果:

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;

当尝试这段代码时,我有一个惊喜:它不会在文件的末尾停止,它只会继续下去,永远。它永远不会失败。说实话,我不确定它是否会失败……它可能不应该失败。无论如何,当我们超过文件末尾时,在该循环中执行 READ 操作会失败,因此我们可以使用非常hacky 的混合方法来处理这种情况。

解决该问题的现成例程

下面是一个现成的例程,它获取作为文件打开的物理磁盘的大小,即使 GetFileSize 失败,并且 SetFilePointerFILE_END 失败。将打开的 TFileStream 传递给它,它将返回 Int64 形式的大小:

function Hacky_GetStreamSize(F: TFileStream): Int64;
var Step:DWORD;

    StartPos: Int64;
    StartPos_DWORD: packed array [0..1] of DWORD absolute StartPos;

    KnownGoodPosition: Int64;
    KGP_DWORD: packed array [0..1] of DWORD absolute KnownGoodPosition;

    Dummy:DWORD;

    Block:array[0..512-1] of Byte;
begin
  // Get starting pointer position
  StartPos := 0;
  StartPos_DWORD[0] := SetFilePointer(F.Handle, 0, @StartPos_DWORD[1], FILE_CURRENT);
  try
    // Move file pointer to the first byte
    SetFilePointer(F.Handle, 0, nil, FILE_BEGIN);
    // Init
    KnownGoodPosition := 0;
    Step := 1024 * 1024 * 1024; // Initial step will be 1Gb
    while Step > 512 do
    begin
      // Try to move
      Dummy := 0;
      SetFilePointer(F.Handle, Step, @Dummy, FILE_CURRENT);
      // Test: Try to read!
      if F.Read(Block, 512) = 512 then
        begin
          // Ok! Save the last known good position
          KGP_DWORD[1] := 0;
          KGP_DWORD[0] := SetFilePointer(F.Handle, 0, @KGP_DWORD[1], FILE_CURRENT);
        end
      else
        begin
          // Read failed! Move back to the last known good position and make Step smaller
          SetFilePointer(F.Handle, KGP_DWORD[0], @KGP_DWORD[1], FILE_BEGIN);
          Step := Step div 4; // it's optimal to devide by 4
        end;
    end;
    // From here on we'll use 512 byte steps until we can't read any more
    SetFilePointer(F.Handle, KGP_DWORD[0], @KGP_DWORD[1], FILE_BEGIN);
    while F.Read(Block, 512) = 512 do
      KnownGoodPosition := KnownGoodPosition + 512;
    // Done!
    Result := KnownGoodPosition;
  finally
    // Move file pointer back to starting position
    SetFilePointer(F.Handle, StartPos_DWORD[0], @StartPos_DWORD[1], FILE_BEGIN);
  end;
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;

最后的注释

我提供的 3 个例程作为一个参数 TFileStream,因为我用它来读取和写入文件。显然,它们只使用 TFileStream.Handle,因此可以简单地用文件句柄替换参数:功能将保持不变。

Alternative coding

Suicide, first of all your approach is wrong, and because of your wrong approach you ran into some hairy problems with the way Windows handles Disk drives opened as files. In pseudo code your approach seems to be:

Size = GetFileSize;
for i=0 to (Size / 512) do
begin
  Seek(i * 512);
  ReadBlock;
  WriteBlockToFile;
end;

That's functionally correct, but there's a simpler way to do the same without actually getting the SizeOfDisk and without seeking. When reading something from a file (or a stream), the "pointer" is automatically moved with the ammount of data you just read, so you can skip the "seek". All the functions used to read data from a file return the amount of data that was actually read: you can use that to know when you reached the end of the file without knowing the size of the file to start with!

Here's an idea of how you can read an physical disk to a file, without knowing much about the disk device, using Delphi's 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;

You can obviously do something similar the other way around, reading from a file and writing to disk. Before writing that code I actually attempted doing it your way (getting the file size, etc), and immediately ran into problems! Apparently Windows doesn't know the exact size of the "file", not unless you read from it.

Problems with disks opened as files

For all my testing I used this simple code as the base:

var F: TFileStream;
begin
  F := TFileStream.Create('\\.\PhysicalDrive0', fmOpenRead);
  try
    // Test code goes here...
  finally F.Free;
  end;
end;

The first (obvious) thing to try was:

ShowMessage(IntToStr(DiskStream.Size));

That fails. In the TFileStream implementation that depends on calling FileSeek, and FileSeek can't handle files larger then 2Gb. So I gave GetFileSize a try, using this code:

var RetSize, UpperWord:DWORD;
RetSize := GetFileSize(F.Handle, @UpperWord);
ShowMessage(IntToStr(UpperWord) + ' / ' + IntToStr(RetSize));

That also fails, even those it should be perfectly capable of returning file size as an 64 bit number! Next I tried using the SetFilePointer API, because that's also supposed to handle 64bit numbers. I thought I'd simply seek to the end of the file and look at the result, using this code:

var RetPos, UpperWord:DWORD;
UpperWord := 0;
RetPos := SetFilePos(F.Handle, 0, @UpperWord, FILE_END);
ShowMessage(IntToStr(UpperWord) + ' / ' + IntToStr(RetPos));

This code also fails! And now I'm thinking, why did the first code work? Apparently reading block-by-block works just fine and Windows knows exactly when to stop reading!! So I thought maybe there's a problem with the implementation of the 64 bit file handling routines, let's try seeking to end of the file in small increments; When we get an error seeking we know we reached the end we'll stop:

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;

When trying this code I had a surprise: It doesn't stop at the of the file, it just goes on and on, for ever. It never fails. To be perfectly honest I'm not sure it's supposed to ever fail... It's probably not supposed to fail. Anyway, doing a READ in that loop fails when we're past the end of file so we can use a VERY hacky mixed approach to handle this situation.

Ready-made routines that work around the problem

Here's the ready-made routine that gets the size of the physical disk opened as a file, even when GetFileSize fails, and SetFilePointer with FILE_END fails. Pass it an opened TFileStream and it will return the size as an Int64:

function Hacky_GetStreamSize(F: TFileStream): Int64;
var Step:DWORD;

    StartPos: Int64;
    StartPos_DWORD: packed array [0..1] of DWORD absolute StartPos;

    KnownGoodPosition: Int64;
    KGP_DWORD: packed array [0..1] of DWORD absolute KnownGoodPosition;

    Dummy:DWORD;

    Block:array[0..512-1] of Byte;
begin
  // Get starting pointer position
  StartPos := 0;
  StartPos_DWORD[0] := SetFilePointer(F.Handle, 0, @StartPos_DWORD[1], FILE_CURRENT);
  try
    // Move file pointer to the first byte
    SetFilePointer(F.Handle, 0, nil, FILE_BEGIN);
    // Init
    KnownGoodPosition := 0;
    Step := 1024 * 1024 * 1024; // Initial step will be 1Gb
    while Step > 512 do
    begin
      // Try to move
      Dummy := 0;
      SetFilePointer(F.Handle, Step, @Dummy, FILE_CURRENT);
      // Test: Try to read!
      if F.Read(Block, 512) = 512 then
        begin
          // Ok! Save the last known good position
          KGP_DWORD[1] := 0;
          KGP_DWORD[0] := SetFilePointer(F.Handle, 0, @KGP_DWORD[1], FILE_CURRENT);
        end
      else
        begin
          // Read failed! Move back to the last known good position and make Step smaller
          SetFilePointer(F.Handle, KGP_DWORD[0], @KGP_DWORD[1], FILE_BEGIN);
          Step := Step div 4; // it's optimal to devide by 4
        end;
    end;
    // From here on we'll use 512 byte steps until we can't read any more
    SetFilePointer(F.Handle, KGP_DWORD[0], @KGP_DWORD[1], FILE_BEGIN);
    while F.Read(Block, 512) = 512 do
      KnownGoodPosition := KnownGoodPosition + 512;
    // Done!
    Result := KnownGoodPosition;
  finally
    // Move file pointer back to starting position
    SetFilePointer(F.Handle, StartPos_DWORD[0], @StartPos_DWORD[1], FILE_BEGIN);
  end;
end;

To be complete, here are two routines that may be used to set and get the file pointer using Int64 for positioning:

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;

Last notes

The 3 routines I'm providing take as a parameter an TFileStream, because that's what I use for file reading and writing. They obviously only use TFileStream.Handle, so the parameter can simply be replaced with an file handle: the functionality would stay the same.

℉絮湮 2024-10-16 19:07:19

我知道这个线程很旧,但是...

一个小建议 - 如果您使用 Windows DeviceIoControl(...) 函数,您可以获得驱动器几何和/或分区信息,并使用它们来获取打开的驱动器或分区的总大小/长度。不再需要不断地寻找设备的末端。

这些 IOCTL 还可用于为您提供正确的卷扇区大小,您可以使用它而不是在任何地方默认为 512。

I know this thread is old, but...

One small suggestion - if you use the Windows DeviceIoControl(...) function you can get Drive Geometry and/or Partition Information, and use them to get the total size/length of the opened drive or partition. No more messing around with incrementally seeking to the end of the device.

Those IOCTLs can also be used to give you the correct volume sector size, and you could use that instead of defaulting to 512 everywhere.

多情癖 2024-10-16 19:07:19

非常非常有用。但对于大于 4 GB 的磁盘我遇到了问题。
我解决了

   // Ok! Save the last known good position
      KGP_DWORD[1] := 0;
      KGP_DWORD[0] := SetFilePointer(F.Handle, 0, @KGP_DWORD[1], FILE_CURRENT);

用以下内容替换::

  // Ok! Save the last known good position
  KnownGoodPosition := KnownGoodPosition + Step;

再次非常感谢...

也非常感谢 James R. Twine。我遵循使用 IOCTL_DISK_GET_DRIVE_GEOMETRY_EX 的建议并获得了磁盘尺寸,没有问题,也没有奇怪的解决方法。
这是代码:

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;

Very very useful. But I got a problem for disks greater then 4 GB.
I solved replacing:

   // Ok! Save the last known good position
      KGP_DWORD[1] := 0;
      KGP_DWORD[0] := SetFilePointer(F.Handle, 0, @KGP_DWORD[1], FILE_CURRENT);

with the following:

  // Ok! Save the last known good position
  KnownGoodPosition := KnownGoodPosition + Step;

Many thanks again...

And many thanks also to James R. Twine. I followed the advice of using IOCTL_DISK_GET_DRIVE_GEOMETRY_EX and got disk dimension with no problem and no strange workaround.
Here is the code:

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;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文