我想要复制在任何给定文件中展示的 Windows资源管理器 -> 属性对话框 -> 常规属性页 中的行为。具体来说,我想要复制“磁盘上占用空间大小”字段的精确值。
我想要复制在任何给定文件中展示的 Windows资源管理器 -> 属性对话框 -> 常规属性页 中的行为。具体来说,我想要复制“磁盘上占用空间大小”字段的精确值。
GetFileInformationByHandleEx
,但似乎您需要使用FILE_STANDARD_INFO
或FILE_ID_BOTH_DIR_INFO
。您想要的信息在每个结构体的AllocationSize
成员中返回,但第二个结构体是用于目录句柄,以列出其中的文件而不是目录本身(注意:不是递归的,只是顶级的)。为了方便起见,FILE_STANDARD_INFO
有一个Directory
布尔值,因此如果您不确定,请先调用它。根据FILE_ID_BOTH_DIR_INFO
的文档,
这似乎给出了AllocationSize 包含指定文件分配的空间大小,单位为字节。该值通常是底层物理设备扇区或簇大小的倍数。
磁盘上的大小
信息。FILE_ID_BOTH_DIR_INFO
结构的Delphi翻译。困难似乎在于最后一个成员WCHAR FileName[1]
,描述如下:
我不确定在Delphi中如何处理这个问题。FileName[1]
包含文件名字符串的第一个字符。其后是字符串的其余部分。
Raymond Chen的Windows Confidential文章描述了如何计算该值。最相关的段落如下:
磁盘上的大小测量更加复杂。如果驱动器支持压缩(通过GetVolumeInformation函数返回的FILE_FILE_COMPRESSION标志报告),并且文件已经被压缩或稀疏(FILE_ATTRIBUTE_COMPRESSED,FILE_ATTRIBUTE_SPARSE_FILE),那么文件的磁盘占用大小是由GetCompressedFileSize函数报告的值。这个函数报告文件的压缩大小(如果已经压缩)或者文件减去那些被取消分配并逻辑上视为零的部分(如果是稀疏)。如果文件既没有被压缩也不是稀疏的,则磁盘上的大小是由FindFirstFile函数报告的文件大小向上舍入到最近的簇。
GetCompressedFileSize
返回的是任何卷类型的普通/压缩/稀疏文件的实际大小,因此您可以依靠此函数返回“磁盘上的文件大小”(Windows资源管理器将此值显示为卷簇大小因子),并使用GetFileSize
函数获取“文件大小”。GetCompressedFileSize
的MSDN文档中得知:
如果该文件不位于支持压缩或稀疏文件的卷上,或者该文件不是压缩的或稀疏文件,则获得的值是实际文件大小,与调用GetFileSize返回的值相同。
所以按照以下代码逻辑即可 (在Windows XP上测试过FAT32/FAT/CDfs文件):procedure FileSizeEx(const FileName: string; out Size, SizeOnDisk: UINT);
var
Drive: string;
FileHandle: THandle;
SectorsPerCluster,
BytesPerSector,
Dummy: DWORD;
ClusterSize: DWORD;
SizeHigh, SizeLow: DWORD;
begin
Assert(FileExists(FileName));
Drive := IncludeTrailingPathDelimiter(ExtractFileDrive(FileName));
if not GetDiskFreeSpace(PChar(Drive), SectorsPerCluster, BytesPerSector, Dummy, Dummy) then
RaiseLastOSError;
ClusterSize := SectorsPerCluster * BytesPerSector;
FileHandle := CreateFile(PChar(FileName), 0, FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE,
nil, OPEN_EXISTING, 0, 0);
if (FileHandle = INVALID_HANDLE_VALUE) then
RaiseLastOSError;
try
SizeLow := Windows.GetFileSize(FileHandle, @SizeHigh);
if (GetLastError <> NO_ERROR) and (SizeLow = INVALID_FILE_SIZE) then
RaiseLastOSError;
Size := UINT(SizeHigh shl 32 or SizeLow);
finally
if (FileHandle <> INVALID_HANDLE_VALUE) then
CloseHandle(FileHandle);
end;
SizeLow := GetCompressedFileSize(PChar(FileName), @SizeHigh);
if (GetLastError <> NO_ERROR) and (SizeLow = INVALID_FILE_SIZE) then
RaiseLastOSError;
SizeOnDisk := UINT(SizeHigh shl 32 or SizeLow);
if (SizeOnDisk mod ClusterSize) > 0 then
SizeOnDisk := SizeOnDisk + ClusterSize - (SizeOnDisk mod ClusterSize);
end;
我们可以通过检查GetVolumeInformation
是否支持压缩/稀疏,然后使用GetFileAttributes
来测试FILE_ATTRIBUTE_COMPRESSED
或FILE_ATTRIBUTE_SPARSE_FILE
,但是由于GetCompressedFileSize
已经在内部为我们执行了这些测试(通过调用NtQueryInformationFile
),所以我认为这些测试没有意义。
CompressedFileSize
字段即为你需要的值(与GetCompressedFileSize
返回值相同)。请注意保留HTML标签,最好让内容更加易于理解。根据David从Raymond的文章中提取的例程进行发布。 随意改进它!
uses
System.SysUtils, Windows;
function GetClusterSize(Drive: String): integer;
var
SectorsPerCluster, BytesPerSector, dummy: Cardinal;
begin
SectorsPerCluster := 0;
BytesPerSector := 0;
GetDiskFreeSpace(PChar(Drive), SectorsPerCluster, BytesPerSector, dummy, dummy);
Result := SectorsPerCluster * BytesPerSector;
end;
function FindSizeOnDisk(Drive: String; AFilename: string): Int64;
var
VolumeSerialNumber: DWORD;
MaximumComponentLength: DWORD;
FileSystemFlags: DWORD;
HighSize: DWORD;
FRec: TSearchRec;
AClusterSize: integer;
AFileSize, n: Int64;
begin
Result := 0;
Drive := IncludeTrailingPathDelimiter(ExtractFileDrive(Drive));
GetVolumeInformation( PChar(Drive), nil, 0, @VolumeSerialNumber,
MaximumComponentLength, FileSystemFlags, nil, 0);
if ((FileSystemFlags AND FILE_FILE_COMPRESSION) <> 0) AND
((FileSystemFlags AND (FILE_VOLUME_IS_COMPRESSED OR
FILE_SUPPORTS_SPARSE_FILES)) <> 0) then
begin // Compressed or Sparse disk
Result := GetCompressedFileSize(PChar(AFilename), @HighSize);
// Not sure if this is correct on a sparse disk ??
end
else
begin
if (System.SysUtils.FindFirst(AFilename, faAnyFile, FRec) = 0) then
begin
AFileSize := FRec.Size;
AClusterSize := GetClusterSize(Drive);
n := AFileSize mod AClusterSize;
if n > 0 then // Round up to nearest cluster size
Result := AFileSize + (AClusterSize - n)
else
Result := AFileSize;
System.SysUtils.FindClose(FRec);
end;
end;
end;
GetCompressedFileSize
不是正确的函数”时,您能否详细说明一下? - David HeffernanGetCompressedFileSize
出了什么问题,或者你从这样的函数中期望得到什么(最好提供一个例子,比如我有一个0B的文件,在资源管理器中我可以看到磁盘上的文件大小也是0B,但是GetCompressedFileSize
返回其他结果)。 - TLama