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"?
risposta
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.
Vista + solo in base ai documenti. – kobik
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. –
Oppure qualsiasi compilatore C o C++, ad es.i compilatori MS distribuiti nel Platform SDK –
è possibile utilizzare la funzione di GetFileInformationByHandleEx per ottenere FILE_COMPRESSION_INFO struttura, il suo campo CompressedFileSize
è il valore necessario (lo stesso restituito da GetCompressedFileSize
).
Anche se si avverte che l'utilizzo di questo su XP non è banale –
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
@ 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. –
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.
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;
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 GetVolumeInformation
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 .
Puoi ampliare ciò che intendi quando dici che "GetCompressedFileSize" non è la funzione corretta? –
@DavidHeffernan, intendevo consigliare un esperto contro l'ingresso nella stessa fossa apparentemente ovvia dopo di me ... – OnTheFly
Sei sicuro che non funzioni. Forse non sei riuscito a combinare correttamente il DWORD basso e alto. –