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.