由于GetCompressedFileSize 将返回 any 卷类型的普通/压缩/备用文件的实际大小,因此您可以依靠此函数返回 File Size on Disk(Windows 资源管理器正在显示此值作为体积簇大小的因子),并使用GetFileSize 函数获得File Size。
来自关于 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),我认为这些测试没有意义。