Delphi Pascal - 使用 SetFilePointerEx 和 GetFileSizeEx,在读取文件时获取物理媒体的确切大小
我不知道如何使用 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
我建议您看一下这篇 Primoz Gabrijelcic 博客文章 和他的 < href="http://gp.17slon.com/gp/gphugefile.htm">GpHugeFile 单元应该为您提供足够的指针来获取文件大小。
编辑 1 鉴于对问题的编辑,现在这看起来是一个相当愚蠢的答案。
编辑2 既然这个答案已被接受,在对 jachguate 的答案进行了大量评论之后,我觉得有责任总结一下所学到的内容。
GetFileSize
和SetFilePointer
没有 2GB限制,它们可以用于文件
基本上是任意大小。
GetFileSizeEx
和SetFilePointerEx
非常多更容易使用,因为它们有效
直接使用 64 位数量和
错误条件要简单得多
信号。
OP实际上并不需要
计算他的磁盘大小。自从
OP正在阅读整个
磁盘内容的大小不是
需要。所需要的只是
依次读取内容,直到
什么都没有剩下。
事实上
GetFileSize
/GetFileSizeEx
不支持设备句柄
(例如物理磁盘或卷)作为
应OP的要求。更重要的是,
SetFilePointer
/SetFilePointerEx
无法找到此类设备的末尾
句柄。
为了获取a的大小
磁盘、卷或分区,应该
通过该
IOCTL_DISK_GET_LENGTH_INFO
控制代码到
DeviceIoControl
。最后,如果您需要使用
GetFileSizeEx
< /a> 和SetFilePointerEx
那么它们可以声明如下:获取这些 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
andSetFilePointer
have no 2GBlimitation, they can be used on files
of essentially arbitrary size.
GetFileSizeEx
andSetFilePointerEx
are mucheasier 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
andSetFilePointerEx
then they can be declared as follows:One easy way to obtain these API imports them is through the excellent JEDI API Library.
GetFileSizeEx 例程需要一个指向LARGE_INTEGER 数据类型和文档说:
幸运的是,Delphi 内置了对 64 位整数的支持,所以使用它:
SetFilePointerEx 需要 liDistanceToMove 的参数, lpNewFilePointer,均为 64 位整数。我的理解是它需要有符号整数,但如果我误解了文档,则您有 Unsingned 64 位整数的 UInt64 数据类型。
The GetFileSizeEx routine expects a pointer to a LARGE_INTEGER data type, and documentation says:
Lucky you, Delphi has built-in support for 64 bit integers, so use it:
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.
替代编码
自杀,首先你的方法是错误的,并且由于你的错误方法,你在 Windows 处理作为文件打开的磁盘驱动器的方式上遇到了一些棘手的问题。在伪代码中,您的方法似乎是:
这在功能上是正确的,但是有一种更简单的方法可以执行相同的操作,而无需实际获取 SizeOfDisk 且无需查找。当从文件(或流)中读取某些内容时,“指针”会随着您刚刚读取的数据量自动移动,因此您可以跳过“查找”。用于从文件读取数据的所有函数都会返回实际读取的数据量:您可以使用它来知道何时到达文件末尾,而无需知道文件的开始大小!
下面是如何使用 Delphi 的 TFileStream 将物理磁盘读取到文件的想法,而无需了解太多有关磁盘设备的信息:
显然,您可以以相反的方式执行类似的操作,从文件读取并写入磁盘。在编写该代码之前,我实际上尝试按照您的方式进行操作(获取文件大小等),但立即遇到了问题!显然,Windows 不知道“文件”的确切大小,除非您读取它。
作为文件打开磁盘的问题
对于我的所有测试,我使用这个简单的代码作为基础:
第一个(明显的)要尝试的事情是:
失败了。在 TFileStream 实现中依赖于调用 FileSeek,而 FileSeek 无法处理大于 2Gb 的文件。因此,我使用以下代码尝试了 GetFileSize:
这也失败了,即使它应该完全能够以 64 位数字形式返回文件大小!接下来我尝试使用 SetFilePointer API,因为它也应该处理 64 位数字。我想我只需使用以下代码查找文件末尾并查看结果:
此代码也失败了!现在我在想,为什么第一个代码可以工作?显然,逐块读取效果很好,Windows 确切地知道何时停止读取!所以我认为 64 位文件处理例程的实现可能存在问题,让我们尝试以较小的增量寻找文件结尾;当我们在查找时遇到错误时,我们知道我们到达了末尾,我们将停止:
当尝试这段代码时,我有一个惊喜:它不会在文件的末尾停止,它只会继续下去,永远。它永远不会失败。说实话,我不确定它是否会失败……它可能不应该失败。无论如何,当我们超过文件末尾时,在该循环中执行 READ 操作会失败,因此我们可以使用非常hacky 的混合方法来处理这种情况。
解决该问题的现成例程
下面是一个现成的例程,它获取作为文件打开的物理磁盘的大小,即使
GetFileSize
失败,并且SetFilePointer
与FILE_END
失败。将打开的 TFileStream 传递给它,它将返回 Int64 形式的大小:为了完整起见,这里有两个例程,可用于使用 Int64 来设置和获取文件指针以进行定位:
最后的注释
我提供的 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:
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:
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:
The first (obvious) thing to try was:
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:
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:
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:
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, andSetFilePointer
withFILE_END
fails. Pass it an opened TFileStream and it will return the size as an Int64:To be complete, here are two routines that may be used to set and get the file pointer using Int64 for positioning:
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.
我知道这个线程很旧,但是...
一个小建议 - 如果您使用 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.
非常非常有用。但对于大于 4 GB 的磁盘我遇到了问题。
我解决了
用以下内容替换::
再次非常感谢...
也非常感谢 James R. Twine。我遵循使用 IOCTL_DISK_GET_DRIVE_GEOMETRY_EX 的建议并获得了磁盘尺寸,没有问题,也没有奇怪的解决方法。
这是代码:
Very very useful. But I got a problem for disks greater then 4 GB.
I solved replacing:
with the following:
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: