无法读取 Windows 7 64 位上最后几 Kb 的逻辑驱动器
Unable to read final few Kb of logical drives on Windows 7 64-bit
我在处理逻辑驱动器时遇到了一些问题。为清楚起见,我对 'Physical Disk' (PD) 的定义是与分区无关的原始磁盘。 'Logical Drive' (LD) 指的是一个卷,例如驱动器 E:、驱动器 F: 等。
使用 RRUZ(我的英雄 SO 成员)的示例并实施 WMI Class I have created a Freepascal program for reading disks. I address PD by \.\PhyscialDiskX and that works fine by the examples created by RRUZ (here)。我可以读取所有字节,对于 PD 来说没有问题。
我对逻辑卷使用相同的句柄技术,即\?\E:或\?\F:等。然后我使用IOCTL_DISK_GET_LENGTH_INFO获取PD或LV的长度,然后读取字节范围,直到 ReadBytes = TotalLength。我在 MSDN 网站上读到它会自动检索传递给它的任何设备句柄的大小——PD 或 LD 等。事实上,我已经检查了我的程序返回的 szie 值 againzt WinHex、FTK Imager、HxD 和其他几个低级磁盘工具。除了由 0 个或 1 个起始位置引起的 1 字节差异外,它们是匹配的。
但是,由于某些原因,尽管 运行 程序是管理员,但我的程序无法在 Windows 7 Pro 64 位上获取最后的 32Kb。它读取整个磁盘,然后读取最终缓冲区(作为 64Kb 缓冲区完成)BytesRead returns -1。使用调试器我计算出以下值:
493,846,527 exact LV size of Drive F:
493,813,760 total bytes read at time of failure
32,767 bytes missing
下面的结果
BytesRead := FileRead(hDiskHandle, Buffer, (DiskSize - TotalBytesRead));
在最终缓冲区读取时为 -1。这是通过 "if the amount left to read is less than the size of the buffer size, only try to read what is left" 测试磁盘末尾的行。因此,最后 FileRead 要求存储的字节值为 32,767(因为此时 DiskSize - TotalBytesRead 为 32,767,这意味着磁盘还剩下多少字节要读取)。 缓冲区的指定大小为64Kb。我的理解是,您可以在缓冲区中放入比它能够容纳的更少 but not more(FileRead 状态:“缓冲区必须至少 Count 个字节长。不对此执行检查"? 正确吗?如果不正确,那么这可能是(并且可能是)问题所在。
不知道是IOCTL_DISK_GET_LENGTH_INFO、缓存还是其他原因?希望有人能帮忙?我还在 Lazarus Freepascal forum. 上发布了一些屏幕截图,这是我的相关代码部分:
句柄:
// Create handle to source disk. Abort if fails
hSelectedDisk := CreateFileW(PWideChar(SourceDevice), FILE_READ_DATA,
FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0);
if hSelectedDisk = INVALID_HANDLE_VALUE then
begin
RaiseLastOSError;
end
计算给定设备的大小离子字节:
ExactDiskSize := GetDiskLengthInBytes(hSelectedDisk);
现在读取设备并将输入存储为平面文件
ImageResult := WindowsImageDisk(hSelectedDisk, ExactDiskSize, HashChoice, hImageName);
上述函数:
function GetDiskLengthInBytes(hSelectedDisk : THandle) : Int64;
const
// These are defined at the MSDN.Microsoft.com website for DeviceIOControl
// and https://forum.tuts4you.com/topic/22361-deviceiocontrol-ioctl-codes/
{
IOCTL_DISK_GET_DRIVE_GEOMETRY = [=16=]70000
IOCTL_DISK_GET_PARTITION_INFO = [=16=]74004
IOCTL_DISK_SET_PARTITION_INFO = [=16=]7C008
IOCTL_DISK_GET_DRIVE_LAYOUT = [=16=]7400C
IOCTL_DISK_SET_DRIVE_LAYOUT = [=16=]7C010
IOCTL_DISK_VERIFY = [=16=]70014
IOCTL_DISK_FORMAT_TRACKS = [=16=]7C018
IOCTL_DISK_REASSIGN_BLOCKS = [=16=]7C01C
IOCTL_DISK_PERFORMANCE = [=16=]70020
IOCTL_DISK_IS_WRITABLE = [=16=]70024
IOCTL_DISK_LOGGING = [=16=]70028
IOCTL_DISK_FORMAT_TRACKS_EX = [=16=]7C02C
IOCTL_DISK_HISTOGRAM_STRUCTURE = [=16=]70030
IOCTL_DISK_HISTOGRAM_DATA = [=16=]70034
IOCTL_DISK_HISTOGRAM_RESET = [=16=]70038
IOCTL_DISK_REQUEST_STRUCTURE = [=16=]7003C
IOCTL_DISK_REQUEST_DATA = [=16=]70040
IOCTL_DISK_CONTROLLER_NUMBER = [=16=]70044
IOCTL_DISK_GET_PARTITION_INFO_EX = [=16=]70048
IOCTL_DISK_SET_PARTITION_INFO_EX = [=16=]7C04C
IOCTL_DISK_GET_DRIVE_LAYOUT_EX = [=16=]70050
IOCTL_DISK_SET_DRIVE_LAYOUT_EX = [=16=]7C054
IOCTL_DISK_CREATE_DISK = [=16=]7C058
IOCTL_DISK_GET_LENGTH_INFO = [=16=]7405C // Our constant...
SMART_GET_VERSION = [=16=]74080
SMART_SEND_DRIVE_COMMAND = [=16=]7C084
SMART_RCV_DRIVE_DATA = [=16=]7C088
IOCTL_DISK_GET_DRIVE_GEOMETRY_EX = [=16=]700A0
IOCTL_DISK_UPDATE_DRIVE_SIZE = [=16=]7C0C8
IOCTL_DISK_GROW_PARTITION = [=16=]7C0D0
IOCTL_DISK_GET_CACHE_INFORMATION = [=16=]740D4
IOCTL_DISK_SET_CACHE_INFORMATION = [=16=]7C0D8
IOCTL_DISK_GET_WRITE_CACHE_STATE = [=16=]740DC
IOCTL_DISK_DELETE_DRIVE_LAYOUT = [=16=]7C100
IOCTL_DISK_UPDATE_PROPERTIES = [=16=]70140
IOCTL_DISK_FORMAT_DRIVE = [=16=]7C3CC
IOCTL_DISK_SENSE_DEVICE = [=16=]703E0
IOCTL_DISK_INTERNAL_SET_VERIFY = [=16=]70403
IOCTL_DISK_INTERNAL_CLEAR_VERIFY = [=16=]70407
IOCTL_DISK_INTERNAL_SET_NOTIFY = [=16=]70408
IOCTL_DISK_CHECK_VERIFY = [=16=]74800
IOCTL_DISK_MEDIA_REMOVAL = [=16=]74804
IOCTL_DISK_EJECT_MEDIA = [=16=]74808
IOCTL_DISK_LOAD_MEDIA = [=16=]7480C
IOCTL_DISK_RESERVE = [=16=]74810
IOCTL_DISK_RELEASE = [=16=]74814
IOCTL_DISK_FIND_NEW_DEVICES = [=16=]74818
IOCTL_DISK_GET_MEDIA_TYPES = [=16=]70C00
}
IOCTL_DISK_GET_LENGTH_INFO = [=16=]07405C;
type
TDiskLength = packed record
Length : Int64;
end;
var
BytesReturned: DWORD;
DLength: TDiskLength;
ByteSize: int64;
begin
BytesReturned := 0;
// Get the length, in bytes, of the physical disk
if not DeviceIOControl(hSelectedDisk, IOCTL_DISK_GET_LENGTH_INFO, nil, 0,
@DLength, SizeOf(TDiskLength), BytesReturned, nil) then
raise Exception.Create('Unable to determine byte capacity of disk.');
ByteSize := DLength.Length;
ShowMessage(IntToStr(ByteSize));
result := ByteSize;
end;
磁盘reader函数
function WindowsImageDisk(hDiskHandle : THandle; DiskSize : Int64; HashChoice : Integer; hImageName : THandle) : Int64;
var
Buffer : array [0..65535] of Byte; // 1048576 (1Mb) or 262144 (240Kb) or 131072 (120Kb buffer) or 65536 (64Kb buffer)
BytesRead : integer;
NewPos, SectorCount,
TotalBytesRead, BytesWritten, TotalBytesWritten : Int64;
...
// Now to seek to start of device
FileSeek(hDiskHandle, 0, 0);
repeat
// Read device in buffered segments. Hash the disk and image portions as we go
if (DiskSize - TotalBytesRead) < SizeOf(Buffer) then
begin
// Read 65535 or less bytes
BytesRead := FileRead(hDiskHandle, Buffer, (DiskSize - TotalBytesRead));
BytesWritten := FileWrite(hImageName, Buffer, BytesRead);
end
else
begin
// Read 65536 (64kb) at a time
BytesRead := FileRead(hDiskHandle, Buffer, SizeOf(Buffer));
BytesWritten := FileWrite(hImageName, Buffer, BytesRead);
end;
if BytesRead = -1 then
begin
ShowMessage('There was a read error encountered. Aborting');
// ERROR IS THROWN AT THIS POINT ONLY WITH LD's - not PD's
exit;
end
else
begin
inc(TotalBytesRead, BytesRead);
inc(TotalBytesWritten, BytesWritten);
NewPos := NewPos + BytesRead;
...
until (TotalBytesRead = DiskSize);
我怀疑问题源于使用 64 位整数和算术来计算作为 32 位整数传递的值:
FileRead(hDiskHandle, Buffer, (DiskSize - TotalBytesRead));
我无法肯定地解释为什么这可能只影响 LD 而不会影响 PD,除了推测报告的 DiskSize 可能存在一些差异,以某种方式避免了 Int64 arithmetic/32-bit 这种情况下的问题。
例如如果 Int64 算法的 32 位截断结果是 NEGATIVE (只需要设置高位,即 1 而不是 0)则FileRead() 将 return -1 因为 "bytes to read" 的负值无效。
但如果结果中的高位未设置,则结果为正值,那么即使该值显着大于 64KB,也不会导致错误因为仅当您已经确定要读取的字节少于 时才会调用此调用。 32 位截断的 Int64 算法可能会导致请求读取 20 亿 字节,但 FileRead() 只会读取实际的 32K 字节无论如何仍然存在。
然而,这个事实指向了一个解决方案(假设这个诊断是正确的)。
如前所述,FileRead()(它只是 ReadFile() 在 Windows 上的包装)将读取指定的字节数 或剩余要读取的字节数 ,以 较低 .
为准
因此,如果您指定 64KB 但只剩下 32KB,那么将只读取 32KB。
您可以替换所有这些代码:
if (DiskSize - TotalBytesRead) < SizeOf(Buffer) then
begin
// Read 65535 or less bytes
BytesRead := FileRead(hDiskHandle, Buffer, (DiskSize - TotalBytesRead));
BytesWritten := FileWrite(hImageName, Buffer, BytesRead);
end
else
begin
// Read 65536 (64kb) at a time
BytesRead := FileRead(hDiskHandle, Buffer, SizeOf(Buffer));
BytesWritten := FileWrite(hImageName, Buffer, BytesRead);
end;
简单地说:
BytesRead := FileRead(hDiskHandle, Buffer, SizeOf(Buffer));
BytesWritten := FileWrite(hImageName, Buffer, BytesRead);
这消除了 64 位算术运算以及因 64 位结果以 32 位值传递而导致错误的任何可能性。如果最后一个段只包含 32KB,那么只会读取 32KB。
您还可以简化循环终止(删除 64 位算法,除非您需要将累加值用于其他目的)。您可以在 FileRead() 读取的字节数少于指定的字节数时终止循环,而不是累加读取的总字节数。即 BytesRead < 64KB:
即使您的磁盘正好是 64KB 块的倍数,倒数第二个 FileRead() 也会 return 64KB 的完整缓冲区,而下一个 FileRead() 将读取 0 字节,即 < 64KB,终止循环。 :)
可能是边界检查错误。引用自 MSDN(CreateFile,关于打开物理驱动器和卷的注释,您称之为逻辑驱动器):
To read or write to the last few sectors of the volume, you must call DeviceIoControl and specify FSCTL_ALLOW_EXTENDED_DASD_IO
32,767 是奇数,不是扇区大小的倍数。这意味着最终部分扇区根本不可读。
好的,我做到了。
归功于 user2024154,因为这是第一件大事。因此,基于此,我在那里给出了答案。
然而,不清楚的是如何正确分配它的 const 值。经过几个小时的谷歌搜索,我偶然发现了 this 。这是我能找到的唯一 Delphi 示例,它实际上显示了 FSCTL_ALLOW_EXTENDED_DASD_IO 已定义,尽管人们必须仔细查看其中的大部分内容才能将值组合在一起。
为了其他人的利益,我现在需要的价值是:
const
FILE_DEVICE_FILE_SYSTEM = [=10=]000009;
FILE_ANY_ACCESS = 0;
METHOD_NEITHER = 3;
FSCTL_ALLOW_EXTENDED_DASD_IO = ((FILE_DEVICE_FILE_SYSTEM shl 16)
or (FILE_ANY_ACCESS shl 14)
or (32 shl 2) or METHOD_NEITHER);
然后我在首先创建句柄之后使用了 FSCTL_ALLOW_EXTENDED_DASD_IO 然后 :
if not DeviceIOControl(hSelectedDisk, FSCTL_ALLOW_EXTENDED_DASD_IO, nil, 0,
nil, 0, BytesReturned, nil) then
raise Exception.Create('Unable to initiate FSCTL_ALLOW_EXTENDED_DASD_IO disk access.');
这适用于 freepascal,稍作调整后应该适用于 Delphi。
感谢大家一直以来的帮助,尤其是 user2024154 和一如既往的 David,感谢他一直以来的帮助。
更新:除了现在物理磁盘访问根本不起作用!但我会想办法的。
我在处理逻辑驱动器时遇到了一些问题。为清楚起见,我对 'Physical Disk' (PD) 的定义是与分区无关的原始磁盘。 'Logical Drive' (LD) 指的是一个卷,例如驱动器 E:、驱动器 F: 等。
使用 RRUZ(我的英雄 SO 成员)的示例并实施 WMI Class I have created a Freepascal program for reading disks. I address PD by \.\PhyscialDiskX and that works fine by the examples created by RRUZ (here)。我可以读取所有字节,对于 PD 来说没有问题。
我对逻辑卷使用相同的句柄技术,即\?\E:或\?\F:等。然后我使用IOCTL_DISK_GET_LENGTH_INFO获取PD或LV的长度,然后读取字节范围,直到 ReadBytes = TotalLength。我在 MSDN 网站上读到它会自动检索传递给它的任何设备句柄的大小——PD 或 LD 等。事实上,我已经检查了我的程序返回的 szie 值 againzt WinHex、FTK Imager、HxD 和其他几个低级磁盘工具。除了由 0 个或 1 个起始位置引起的 1 字节差异外,它们是匹配的。
但是,由于某些原因,尽管 运行 程序是管理员,但我的程序无法在 Windows 7 Pro 64 位上获取最后的 32Kb。它读取整个磁盘,然后读取最终缓冲区(作为 64Kb 缓冲区完成)BytesRead returns -1。使用调试器我计算出以下值:
493,846,527 exact LV size of Drive F:
493,813,760 total bytes read at time of failure
32,767 bytes missing
下面的结果
BytesRead := FileRead(hDiskHandle, Buffer, (DiskSize - TotalBytesRead));
在最终缓冲区读取时为 -1。这是通过 "if the amount left to read is less than the size of the buffer size, only try to read what is left" 测试磁盘末尾的行。因此,最后 FileRead 要求存储的字节值为 32,767(因为此时 DiskSize - TotalBytesRead 为 32,767,这意味着磁盘还剩下多少字节要读取)。 缓冲区的指定大小为64Kb。我的理解是,您可以在缓冲区中放入比它能够容纳的更少 but not more(FileRead 状态:“缓冲区必须至少 Count 个字节长。不对此执行检查"? 正确吗?如果不正确,那么这可能是(并且可能是)问题所在。
不知道是IOCTL_DISK_GET_LENGTH_INFO、缓存还是其他原因?希望有人能帮忙?我还在 Lazarus Freepascal forum. 上发布了一些屏幕截图,这是我的相关代码部分:
句柄:
// Create handle to source disk. Abort if fails
hSelectedDisk := CreateFileW(PWideChar(SourceDevice), FILE_READ_DATA,
FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0);
if hSelectedDisk = INVALID_HANDLE_VALUE then
begin
RaiseLastOSError;
end
计算给定设备的大小离子字节:
ExactDiskSize := GetDiskLengthInBytes(hSelectedDisk);
现在读取设备并将输入存储为平面文件
ImageResult := WindowsImageDisk(hSelectedDisk, ExactDiskSize, HashChoice, hImageName);
上述函数:
function GetDiskLengthInBytes(hSelectedDisk : THandle) : Int64;
const
// These are defined at the MSDN.Microsoft.com website for DeviceIOControl
// and https://forum.tuts4you.com/topic/22361-deviceiocontrol-ioctl-codes/
{
IOCTL_DISK_GET_DRIVE_GEOMETRY = [=16=]70000
IOCTL_DISK_GET_PARTITION_INFO = [=16=]74004
IOCTL_DISK_SET_PARTITION_INFO = [=16=]7C008
IOCTL_DISK_GET_DRIVE_LAYOUT = [=16=]7400C
IOCTL_DISK_SET_DRIVE_LAYOUT = [=16=]7C010
IOCTL_DISK_VERIFY = [=16=]70014
IOCTL_DISK_FORMAT_TRACKS = [=16=]7C018
IOCTL_DISK_REASSIGN_BLOCKS = [=16=]7C01C
IOCTL_DISK_PERFORMANCE = [=16=]70020
IOCTL_DISK_IS_WRITABLE = [=16=]70024
IOCTL_DISK_LOGGING = [=16=]70028
IOCTL_DISK_FORMAT_TRACKS_EX = [=16=]7C02C
IOCTL_DISK_HISTOGRAM_STRUCTURE = [=16=]70030
IOCTL_DISK_HISTOGRAM_DATA = [=16=]70034
IOCTL_DISK_HISTOGRAM_RESET = [=16=]70038
IOCTL_DISK_REQUEST_STRUCTURE = [=16=]7003C
IOCTL_DISK_REQUEST_DATA = [=16=]70040
IOCTL_DISK_CONTROLLER_NUMBER = [=16=]70044
IOCTL_DISK_GET_PARTITION_INFO_EX = [=16=]70048
IOCTL_DISK_SET_PARTITION_INFO_EX = [=16=]7C04C
IOCTL_DISK_GET_DRIVE_LAYOUT_EX = [=16=]70050
IOCTL_DISK_SET_DRIVE_LAYOUT_EX = [=16=]7C054
IOCTL_DISK_CREATE_DISK = [=16=]7C058
IOCTL_DISK_GET_LENGTH_INFO = [=16=]7405C // Our constant...
SMART_GET_VERSION = [=16=]74080
SMART_SEND_DRIVE_COMMAND = [=16=]7C084
SMART_RCV_DRIVE_DATA = [=16=]7C088
IOCTL_DISK_GET_DRIVE_GEOMETRY_EX = [=16=]700A0
IOCTL_DISK_UPDATE_DRIVE_SIZE = [=16=]7C0C8
IOCTL_DISK_GROW_PARTITION = [=16=]7C0D0
IOCTL_DISK_GET_CACHE_INFORMATION = [=16=]740D4
IOCTL_DISK_SET_CACHE_INFORMATION = [=16=]7C0D8
IOCTL_DISK_GET_WRITE_CACHE_STATE = [=16=]740DC
IOCTL_DISK_DELETE_DRIVE_LAYOUT = [=16=]7C100
IOCTL_DISK_UPDATE_PROPERTIES = [=16=]70140
IOCTL_DISK_FORMAT_DRIVE = [=16=]7C3CC
IOCTL_DISK_SENSE_DEVICE = [=16=]703E0
IOCTL_DISK_INTERNAL_SET_VERIFY = [=16=]70403
IOCTL_DISK_INTERNAL_CLEAR_VERIFY = [=16=]70407
IOCTL_DISK_INTERNAL_SET_NOTIFY = [=16=]70408
IOCTL_DISK_CHECK_VERIFY = [=16=]74800
IOCTL_DISK_MEDIA_REMOVAL = [=16=]74804
IOCTL_DISK_EJECT_MEDIA = [=16=]74808
IOCTL_DISK_LOAD_MEDIA = [=16=]7480C
IOCTL_DISK_RESERVE = [=16=]74810
IOCTL_DISK_RELEASE = [=16=]74814
IOCTL_DISK_FIND_NEW_DEVICES = [=16=]74818
IOCTL_DISK_GET_MEDIA_TYPES = [=16=]70C00
}
IOCTL_DISK_GET_LENGTH_INFO = [=16=]07405C;
type
TDiskLength = packed record
Length : Int64;
end;
var
BytesReturned: DWORD;
DLength: TDiskLength;
ByteSize: int64;
begin
BytesReturned := 0;
// Get the length, in bytes, of the physical disk
if not DeviceIOControl(hSelectedDisk, IOCTL_DISK_GET_LENGTH_INFO, nil, 0,
@DLength, SizeOf(TDiskLength), BytesReturned, nil) then
raise Exception.Create('Unable to determine byte capacity of disk.');
ByteSize := DLength.Length;
ShowMessage(IntToStr(ByteSize));
result := ByteSize;
end;
磁盘reader函数
function WindowsImageDisk(hDiskHandle : THandle; DiskSize : Int64; HashChoice : Integer; hImageName : THandle) : Int64;
var
Buffer : array [0..65535] of Byte; // 1048576 (1Mb) or 262144 (240Kb) or 131072 (120Kb buffer) or 65536 (64Kb buffer)
BytesRead : integer;
NewPos, SectorCount,
TotalBytesRead, BytesWritten, TotalBytesWritten : Int64;
...
// Now to seek to start of device
FileSeek(hDiskHandle, 0, 0);
repeat
// Read device in buffered segments. Hash the disk and image portions as we go
if (DiskSize - TotalBytesRead) < SizeOf(Buffer) then
begin
// Read 65535 or less bytes
BytesRead := FileRead(hDiskHandle, Buffer, (DiskSize - TotalBytesRead));
BytesWritten := FileWrite(hImageName, Buffer, BytesRead);
end
else
begin
// Read 65536 (64kb) at a time
BytesRead := FileRead(hDiskHandle, Buffer, SizeOf(Buffer));
BytesWritten := FileWrite(hImageName, Buffer, BytesRead);
end;
if BytesRead = -1 then
begin
ShowMessage('There was a read error encountered. Aborting');
// ERROR IS THROWN AT THIS POINT ONLY WITH LD's - not PD's
exit;
end
else
begin
inc(TotalBytesRead, BytesRead);
inc(TotalBytesWritten, BytesWritten);
NewPos := NewPos + BytesRead;
...
until (TotalBytesRead = DiskSize);
我怀疑问题源于使用 64 位整数和算术来计算作为 32 位整数传递的值:
FileRead(hDiskHandle, Buffer, (DiskSize - TotalBytesRead));
我无法肯定地解释为什么这可能只影响 LD 而不会影响 PD,除了推测报告的 DiskSize 可能存在一些差异,以某种方式避免了 Int64 arithmetic/32-bit 这种情况下的问题。
例如如果 Int64 算法的 32 位截断结果是 NEGATIVE (只需要设置高位,即 1 而不是 0)则FileRead() 将 return -1 因为 "bytes to read" 的负值无效。
但如果结果中的高位未设置,则结果为正值,那么即使该值显着大于 64KB,也不会导致错误因为仅当您已经确定要读取的字节少于 时才会调用此调用。 32 位截断的 Int64 算法可能会导致请求读取 20 亿 字节,但 FileRead() 只会读取实际的 32K 字节无论如何仍然存在。
然而,这个事实指向了一个解决方案(假设这个诊断是正确的)。
如前所述,FileRead()(它只是 ReadFile() 在 Windows 上的包装)将读取指定的字节数 或剩余要读取的字节数 ,以 较低 .
为准因此,如果您指定 64KB 但只剩下 32KB,那么将只读取 32KB。
您可以替换所有这些代码:
if (DiskSize - TotalBytesRead) < SizeOf(Buffer) then
begin
// Read 65535 or less bytes
BytesRead := FileRead(hDiskHandle, Buffer, (DiskSize - TotalBytesRead));
BytesWritten := FileWrite(hImageName, Buffer, BytesRead);
end
else
begin
// Read 65536 (64kb) at a time
BytesRead := FileRead(hDiskHandle, Buffer, SizeOf(Buffer));
BytesWritten := FileWrite(hImageName, Buffer, BytesRead);
end;
简单地说:
BytesRead := FileRead(hDiskHandle, Buffer, SizeOf(Buffer));
BytesWritten := FileWrite(hImageName, Buffer, BytesRead);
这消除了 64 位算术运算以及因 64 位结果以 32 位值传递而导致错误的任何可能性。如果最后一个段只包含 32KB,那么只会读取 32KB。
您还可以简化循环终止(删除 64 位算法,除非您需要将累加值用于其他目的)。您可以在 FileRead() 读取的字节数少于指定的字节数时终止循环,而不是累加读取的总字节数。即 BytesRead < 64KB:
即使您的磁盘正好是 64KB 块的倍数,倒数第二个 FileRead() 也会 return 64KB 的完整缓冲区,而下一个 FileRead() 将读取 0 字节,即 < 64KB,终止循环。 :)
可能是边界检查错误。引用自 MSDN(CreateFile,关于打开物理驱动器和卷的注释,您称之为逻辑驱动器):
To read or write to the last few sectors of the volume, you must call DeviceIoControl and specify FSCTL_ALLOW_EXTENDED_DASD_IO
32,767 是奇数,不是扇区大小的倍数。这意味着最终部分扇区根本不可读。
好的,我做到了。
归功于 user2024154,因为这是第一件大事。因此,基于此,我在那里给出了答案。
然而,不清楚的是如何正确分配它的 const 值。经过几个小时的谷歌搜索,我偶然发现了 this 。这是我能找到的唯一 Delphi 示例,它实际上显示了 FSCTL_ALLOW_EXTENDED_DASD_IO 已定义,尽管人们必须仔细查看其中的大部分内容才能将值组合在一起。
为了其他人的利益,我现在需要的价值是:
const
FILE_DEVICE_FILE_SYSTEM = [=10=]000009;
FILE_ANY_ACCESS = 0;
METHOD_NEITHER = 3;
FSCTL_ALLOW_EXTENDED_DASD_IO = ((FILE_DEVICE_FILE_SYSTEM shl 16)
or (FILE_ANY_ACCESS shl 14)
or (32 shl 2) or METHOD_NEITHER);
然后我在首先创建句柄之后使用了 FSCTL_ALLOW_EXTENDED_DASD_IO 然后 :
if not DeviceIOControl(hSelectedDisk, FSCTL_ALLOW_EXTENDED_DASD_IO, nil, 0,
nil, 0, BytesReturned, nil) then
raise Exception.Create('Unable to initiate FSCTL_ALLOW_EXTENDED_DASD_IO disk access.');
这适用于 freepascal,稍作调整后应该适用于 Delphi。
感谢大家一直以来的帮助,尤其是 user2024154 和一如既往的 David,感谢他一直以来的帮助。
更新:除了现在物理磁盘访问根本不起作用!但我会想办法的。