Since GetCompressedFileSize
will return the actual size for normal / compressed / spare files of any type of volume, you can rely on this function to return File Size on Disk
(Windows Explorer displays this value as a cluster volume factor) and get File Size
using GetFileSize
.
From the MSDN GetCompressedFileSize
about GetCompressedFileSize
:
If the file is not located on a volume that supports compression or sparse files, or if the file is not compressed or the sparse file, the resulting value is the actual file size, the same as the return value by calling GetFileSize.
Thus, the logic is described by the following code (tested on Windows XP with FAT32 / FAT / CDfs files):
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;
We could check GetVolumeInformation
to support compression / sparseness, and then GetFileAttributes
to check for FILE_ATTRIBUTE_COMPRESSED
or FILE_ATTRIBUTE_SPARSE_FILE
, BUT, since GetCompressedFileSize
does this inside us (by calling NtQueryInformationFile
), I donβt see the point in these.