【问题标题】:How do I query "Size on disk" file information?如何查询“磁盘大小”文件信息?
【发布时间】:2012-04-11 19:39:31
【问题描述】:

我想为任何给定文件重现 Windows 资源管理器 -> 属性对话框 -> 常规属性页 中显示的行为。具体来说,我想重现“磁盘大小”字段的确切值。

【问题讨论】:

  • 当你说GetCompressedFileSize 不是正确的功能时,你能扩展你的意思吗?
  • @DavidHeffernan,我的意思是建议专家不要在我之后踏入同一个看似明显的坑......
  • 我投票结束,因为我错过了这里的问题。相反,您只需发布许多信息,说明您如何找出某个文件的异常之处以及您的集群大小有多大。仍然没有任何信息,GetCompressedFileSize 有什么问题,或者您希望从此类功能中获得什么(最好的例子是我有一个 0B 文件,在资源管理器中我可以看到 0B 作为磁盘上的文件大小,但GetCompressedFileSize 会返回其他内容)。
  • 我必须同意@TLama。 问题是什么? (请用简单的英语)
  • 问题似乎变了。你能解释一下你尝试了什么以及它是如何失败的吗?

标签: delphi winapi filesystems


【解决方案1】:

正如其他人所说,您需要使用GetFileInformationByHandleEx,但看起来您需要使用FILE_STANDARD_INFOFILE_ID_BOTH_DIR_INFO。您想要的信息在每个的AllocationSize 成员中返回,但第二个是目录句柄,列出其中的文件而不是目录本身(注意:不是递归的,只是顶级的)。为方便起见,FILE_STANDARD_INFO 有一个 Directory 布尔值,因此如果您不确定,请先调用它。根据FILE_ID_BOTH_DIR_INFO 的文档,

分配大小 包含指定为文件分配多少空间的值,以字节为单位。该值通常是底层物理设备的扇区或簇大小的倍数。

这似乎为您提供了Size on Disk 信息。

我还没有找到FILE_ID_BOTH_DIR_INFO 结构的德尔福翻译。困难似乎是最后一个成员WCHAR FileName[1],它被描述为:

文件名[1]
包含文件名字符串的第一个字符。字符串的其余部分在内存中紧随其后。

我不确定在 Delphi 中会如何处理。

【讨论】:

  • 其实,没有。在头文件部分有一条说明说它在 XP 上可用:WinBase.h(包括 Windows.h); Windows Server 2003 和 Windows XP 上的 FileExtd.h,带有要链接的库,因此您可以在 C++Builder 中创建一个包装 DLL,然后通过该包装调用它。
  • 或者实际上是任何 C 或 C++ 编译器,例如平台 SDK 中分布的 MS 编译器
  • FileName[1] 是一个可变长度结构。一旦知道文件名有多长,就必须在堆上为结构分配内存。
【解决方案2】:

Raymond ChenWindows Confidential 上的文章描述了如何计算该值。最相关的段落指出:

磁盘大小测量更复杂。如果驱动器支持压缩(由 GetVolumeInformation 函数返回的 FILE_FILE_COMPRESSION 标志报告)并且文件被压缩或稀疏(FILE_ATTRIBUTE_COMPRESSED、FILE_ATTRIBUTE_SPARSE_FILE),则文件的磁盘大小是 GetCompressedFileSize 函数报告的值。这会报告文件的压缩大小(如果压缩)或文件大小减去已取消提交并在逻辑上视为零(如果稀疏)的部分。如果文件既不压缩也不稀疏,则磁盘上的大小是 FindFirstFile 函数报告的文件大小,四舍五入到最近的簇。

【讨论】:

    【解决方案3】:

    由于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;
    

    我们可以检查Get­Volume­Information 是否支持压缩/稀疏,然后检查GetFileAttributes 以测试FILE_ATTRIBUTE_COMPRESSEDFILE_ATTRIBUTE_SPARSE_FILE,但是因为GetCompressedFileSize 在内部为我们完成了这项工作(通过调用NtQueryInformationFile),我认为这些测试没有意义。

    【讨论】:

      【解决方案4】:

      你可以使用GetFileInformationByHandleEx函数来获取FILE_COMPRESSION_INFO结构,它的CompressedFileSize字段就是你需要的值(和GetCompressedFileSize返回的一样)。

      【讨论】:

      • 虽然被警告在 XP 上使用它是不平凡的
      • 现在我完全困惑了。调用成功返回的 FILE_COMPRESSION_INFO 被全零填充。但是,我终于弄清楚了违规文件的异常之处,更新了问题。
      • @user539484:如果传递 FILE_STANDARD_INFO 然后读取 AllocationSize 会怎样?如果失败,请尝试 FILE_STREAM_INFO 并读取 StreamAllocationSize。仅适用于 Vista 及更高版本。
      • 零?您是否使用 FILE_INFO_BY_HANDLE_CLASS 的参数 FileCompressionInfo 调用了 GetFileInformationByHandleEx更新:随着时间的推移,原始问题的变化有点太大了……你想知道/找到什么?
      【解决方案5】:

      根据 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;
      

      【讨论】:

        猜你喜欢
        • 2013-11-01
        • 2018-12-04
        • 2015-12-03
        • 2013-02-17
        • 1970-01-01
        • 2020-10-17
        • 1970-01-01
        • 2023-03-29
        • 1970-01-01
        相关资源
        最近更新 更多