2012-03-28 12 views
6

Desidero riprodurre il comportamento esposto in Windows Explorer -> Finestra di dialogo Proprietà -> Pagina di proprietà generale per qualsiasi file specificato. Nello specifico voglio riprodurre il valore esatto del campo "Dimensione su disco".Come interrogare le informazioni sul file "Dimensioni su disco"?

+4

Puoi ampliare ciò che intendi quando dici che "GetCompressedFileSize" non è la funzione corretta? –

+0

@DavidHeffernan, intendevo consigliare un esperto contro l'ingresso nella stessa fossa apparentemente ovvia dopo di me ... – OnTheFly

+0

Sei sicuro che non funzioni. Forse non sei riuscito a combinare correttamente il DWORD basso e alto. –

risposta

2

Come altri hanno già detto, è necessario utilizzare GetFileInformationByHandleEx, ma sembra che sia necessario utilizzare FILE_ID_BOTH_DIR_INFO. Le informazioni desiderate vengono restituite nel membro AllocationSize. Secondo il link qui sopra,

AllocationSize Contiene il valore che specifica la quantità di spazio allocato per il file, in byte. Questo valore è solitamente un multiplo del settore o delle dimensioni del cluster del dispositivo fisico sottostante.

Questo sembra fornire le informazioni Size on Disk.

Non ho trovato una traduzione Delphi della struttura FILE_ID_BOTH_DIR_INFO. La difficoltà sembra essere l'ultimo membro, WCHAR FileName[1], che viene descritto come:

FileName [1]
Contiene il primo carattere della stringa nome del file. Questo è seguito in memoria dal resto della stringa.

Non sono sicuro di come sarebbe stato gestito in Delphi.

+1

Vista + solo in base ai documenti. – kobik

+0

In realtà, no. C'è una nota che dice che è disponibile su XP, nella sezione File Header: WinBase.h (include Windows.h); FileExtd.h su Windows Server 2003 e ** Windows XP **, con una libreria da collegare, quindi è possibile creare una DLL wrapper in C++ Builder e quindi chiamarla tramite tale wrapper. –

+0

Oppure qualsiasi compilatore C o C++, ad es.i compilatori MS distribuiti nel Platform SDK –

2

è possibile utilizzare la funzione di GetFileInformationByHandleEx per ottenere FILE_COMPRESSION_INFO struttura, il suo campo CompressedFileSize è il valore necessario (lo stesso restituito da GetCompressedFileSize).

+2

Anche se si avverte che l'utilizzo di questo su XP non è banale –

+0

Ora sono completamente perplesso. FILE_COMPRESSION_INFO restituito dalla chiamata riuscita viene riempito con tutti gli zeri. Tuttavia, ho finalmente capito cosa è insolito con il file offensivo, aggiornando la domanda. – OnTheFly

+0

@ user539484: cosa succede se si passa FILE_STANDARD_INFO e quindi leggere l'AllocationSize? Quando fallisce, prova FILE_STREAM_INFO e leggi StreamAllocationSize. Funziona solo su Vista e versioni successive. –

3

Raymond Chen L'articolo su Windows Confidential descrive come viene calcolato tale valore. Gli stati dei paragrafi più pertinenti:

La dimensione della misurazione del disco è più complicata. Se l'unità supporta la compressione (come riportato dal flag FILE_FILE_COMPRESSION restituito dalla funzione GetVolumeInformation) e il file è compresso o sparse (FILE_ATTRIBUTE_COMPRESSED, FILE_ATTRIBUTE_SPARSE_FILE), la dimensione su disco per un file è il valore riportato dalla funzione GetCompressedFileSize. Questo riporta la dimensione compressa del file (se compressa) o la dimensione del file meno le parti che sono state disimpegnate e logicamente trattate come zero (se sparse). Se il file non è né compresso né sparse, la Dimensione su disco è la dimensione del file riportata dalla funzione FindFirstFile arrotondata al cluster più vicino.

0

Pubblicare una routine in base all'estratto di David dell'articolo di Raymond. Sentiti libero di migliorarlo!

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

Dal GetCompressedFileSize restituirà la dimensione effettiva sia normale/compressa/Ricambi file di qualsiasi tipo volume, è possibile fare affidamento su questa funzione per restituire il File Size on Disk (Esplora risorse di Windows è la visualizzazione di questo valore come fattore di volumi Dimensione Cluster), e ottenere il File Size utilizzando la funzione GetFileSize.

Dalla documentazione MSDN su GetCompressedFileSize:

Se il file non si trova su un volume che supporta la compressione o file sparsi, o se il file non è compresso o di un file sparse, il valore ottenuto è la dimensione effettiva del file, uguale al valore restituito da una chiamata a GetFileSize.

Quindi la logica è descritta dal seguente codice (testato su Windows XP con i file/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; 

Abbiamo potrebbe controllare la Get­Volume­Information per la compressione/sparse supporto, e quindi GetFileAttributes da provare per FILE_ATTRIBUTE_COMPRESSED o FILE_ATTRIBUTE_SPARSE_FILE, MA dal momento che il GetCompressedFileSize lo fa internamente per noi (chiamando il numero NtQueryInformationFile), non vedo alcun punto in questi test .

Problemi correlati