我想重现 Windows资源管理器中显示的行为 - >属性对话框 - >任何给定文件的常规属性页。具体来说,我想重现“磁盘大小”字段的确切值。
答案 0 :(得分:3)
Raymond Chen关于Windows Confidential的文章介绍了如何计算该值。最相关的段落指出:
磁盘测量尺寸更复杂。如果驱动器支持压缩(由GetVolumeInformation函数返回的FILE_FILE_COMPRESSION标志报告)并且文件是压缩的或稀疏的(FILE_ATTRIBUTE_COMPRESSED,FILE_ATTRIBUTE_SPARSE_FILE),则文件的磁盘大小是GetCompressedFileSize函数报告的值。这将报告文件的压缩大小(如果已压缩)或文件大小减去已取消提交并在逻辑上视为零的部分(如果是稀疏的)。如果文件既不是压缩文件也不是稀疏文件,那么磁盘上的大小是FindFirstFile函数报告的文件大小,四舍五入到最近的集群。
答案 1 :(得分:3)
由于GetCompressedFileSize
将返回任何卷类型的普通/压缩/备用文件的实际大小,您可以依赖此函数返回File Size on Disk
(Windows Explorer将此值显示为卷Cluster Size的因子,并使用GetFileSize
函数获取File Size
。
来自MSDN文档GetCompressedFileSize
:
如果文件不在支持压缩的卷上,或者 稀疏文件,或者如果文件没有压缩或稀疏文件, 获得的值是实际文件大小,与返回的值相同 通过调用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
),我认为这些测试没有意义。
答案 2 :(得分:2)
您可以使用GetFileInformationByHandleEx函数获取FILE_COMPRESSION_INFO结构,其CompressedFileSize
字段是您需要的值(与GetCompressedFileSize
返回的相同)。
答案 3 :(得分:2)
正如其他人所说,您需要使用GetFileInformationByHandleEx
,但看起来您需要使用FILE_STANDARD_INFO
或FILE_ID_BOTH_DIR_INFO
。您想要的信息在每个的AllocationSize
成员中返回,但第二个用于目录句柄,列出文件而不是目录本身(注意:不是递归,只是顶级)。为了方便起见,FILE_STANDARD_INFO
有一个Directory
布尔值,如果您不确定,请先调用它。根据{{1}}的文档,
AllocationSize 包含指定为文件分配多少空间的值(以字节为单位)。此值通常是底层物理设备的扇区或簇大小的倍数。
这似乎为您提供了FILE_ID_BOTH_DIR_INFO
信息。
我还没有找到Size on Disk
结构的Delphi翻译。难度似乎是最终成员FILE_ID_BOTH_DIR_INFO
,其描述为:
FileName [1]
包含文件名字符串的第一个字符。在内存中跟随字符串的其余部分。
我不确定如何在Delphi中处理它。
答案 4 :(得分:0)
根据大卫雷蒙德的文章摘录发布例行程序。 随意改进它!
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;