2012-03-28 21 views
6

Quiero reproducir el comportamiento exhibido en el Windows Explorer -> cuadro de diálogo Propiedades -> General de propiedades para cualquier archivo dado. Específicamente quiero reproducir el valor exacto del campo "Tamaño en el disco".¿Cómo consulto la información del archivo "Tamaño en el disco"?

+4

¿Puede ampliar lo que quiere decir cuando dice que 'GetCompressedFileSize' no es la función correcta? –

+0

@DavidHeffernan, quise aconsejar a un experto para que no entrara en el mismo pozo aparentemente obvio después de mí ... – OnTheFly

+0

¿Estás seguro de que no funciona? Tal vez fallaste en combinar las DWORD bajas y altas correctamente. –

Respuesta

2

Como han dicho otros, necesita usar GetFileInformationByHandleEx, pero parece que necesita usar FILE_ID_BOTH_DIR_INFO. La información que desea se devuelve en el miembro AllocationSize. De acuerdo con el enlace de arriba,

allocationSize Contiene el valor que especifica la cantidad de espacio asignado para el archivo, en bytes. Este valor generalmente es un múltiplo del tamaño del sector o clúster del dispositivo físico subyacente.

Esto parece darle la información Size on Disk.

No he encontrado una traducción Delphi de la estructura FILE_ID_BOTH_DIR_INFO. La dificultad parece ser el último miembro, WCHAR FileName[1], que se describe como:

Nombre de archivo [1]
Contiene el primer carácter de la cadena de nombre de archivo. Esto es seguido en memoria por el resto de la cadena.

No estoy seguro de cómo se manejaría esto en Delphi.

+1

Vista + solo de acuerdo con los documentos. – kobik

+0

En realidad, no. Hay una nota que dice que está disponible en XP, en la sección Archivo de encabezado: WinBase.h (incluye Windows.h); FileExtd.h en Windows Server 2003 y ** Windows XP **, con una biblioteca para vincular, por lo que podría crear una DLL contenedora en C++ Builder y luego llamarla a través de esa envoltura. –

+0

O, de hecho, cualquier compilador C o C++, p.los compiladores de MS distribuidos en Platform SDK –

2

Usted puede utilizar la función GetFileInformationByHandleEx obtener FILE_COMPRESSION_INFO estructura, su campo CompressedFileSize es el valor que necesita (igual que el devuelto por GetCompressedFileSize).

+2

Aunque ten en cuenta que usar esto en XP no es trivial –

+0

Ahora estoy completamente perplejo. FILE_COMPRESSION_INFO devuelto de la llamada correcta se completa con todos los ceros. Sin embargo, finalmente descubrí qué es inusual con el archivo ofensivo, actualizando la pregunta. – OnTheFly

+0

@ user539484: ¿y si pasas FILE_STANDARD_INFO y luego lees AllocationSize? Cuando eso falla, pruebe FILE_STREAM_INFO y lea StreamAllocationSize. Solo funciona en Vista y superior. –

3

Raymond Chen El artículo de Windows Confidential describe cómo se calcula ese valor. El párrafo más pertinente dice:

El tamaño en la medición del disco es más complicado. Si el disco admite compresión (como informa el indicador FILE_FILE_COMPRESSION devuelto por la función GetVolumeInformation) y el archivo está comprimido o disperso (FILE_ATTRIBUTE_COMPRESSED, FILE_ATTRIBUTE_SPARSE_FILE), entonces el tamaño en el disco de un archivo es el valor informado por la función GetCompressedFileSize. Esto informa el tamaño comprimido del archivo (si está comprimido) o el tamaño del archivo menos las partes que se eliminaron y se trataron lógicamente como cero (si es escaso). Si el archivo no está comprimido ni es escaso, entonces el Tamaño en el disco es el tamaño del archivo informado por la función FindFirstFile redondeado al clúster más cercano.

0

Publicando una rutina según el extracto de David del artículo de Raymond. ¡Siéntete libre de mejorarlo!

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; 
3

Desde GetCompressedFileSize volverá al tamaño real, tanto por la normalidad/comprimido/repuestos archivos de cualquier tipo volumen, se puede confiar en esta función para devolver el File Size on Disk (Windows Explorer está mostrando este valor como factor de volumen Tamaño del clúster), y obtenga el File Size utilizando la función GetFileSize.

a partir de documentos de MSDN sobre GetCompressedFileSize:

Si el archivo no se encuentra en un volumen que soporta compresión o archivos dispersos, o si el archivo no está comprimido o un archivo disperso, el valor obtenido es el tamaño del archivo real, el mismo que el valor devuelto mediante una llamada a GetFileSize.

Así que la lógica es descrito por el siguiente código (probado en Windows XP con archivos/FAT/CDFS FAT32):

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; 

Nos podría comprobar el Get­Volume­Information para la compresión/escasa soporte, y luego GetFileAttributes para probar FILE_ATTRIBUTE_COMPRESSED o FILE_ATTRIBUTE_SPARSE_FILE, PERO dado que GetCompressedFileSize lo hace internamente para nosotros (llamando a NtQueryInformationFile), no veo sentido en estas pruebas .

Cuestiones relacionadas