7

about get size of a file, i have this two functions:

function GetFileSize1(const FileName: TFileName): Int64;
var
 iTmp: Int64;
 SearchRec: TSearchRec;
begin
  iTmp := -1;
  if FindFirst(FileName, faAnyFile, SearchRec) = 0 then
  begin
    iTmp := SearchRec.Size;
    System.SysUtils.FindClose(SearchRec);
  end;
  Result := iTmp;
end;

And:

function GetFileSize2(const FileName: TFileName): Int64;
var
 FileStream: TFileStream;
begin
  FileStream := TFileStream.Create(FileName, fmOpenRead);
  try
    Result := FileStream.Size; 
  finally  
    FileStream.Free;
  end; 
end;

In practise, what is the difference of it? Both return same result of course but what is more affidable, more fast, more secure? Or better, what is preferible use? First or second? Thanks very much.

Marcello Impastato
  • 2,263
  • 5
  • 30
  • 52

2 Answers2

10

Well, the obvious difference is that GetFileSize2 opens the file, using the CreateFile API to obtain a file handle. In contrast, GetFileSize1 does not because it reads the size from the file meta data.

So I would expect GetFileSize1 to perform better. Although, for many applications that performance difference would not matter. Much more significantly, GetFileSize2 can fail due to a sharing violation in situations that GetFileSize1 will succeed. So you really should not use GetFileSize2.

Note also that the two functions you present behave differently in case of an error: GetFileSize1 returns -1, and GetFileSize2 raises an exception.

Personally I prefer this version:

function GetFileSize3(const FileName: string): Int64;
var
  fad: TWin32FileAttributeData;
begin
  if not GetFileAttributesEx(PChar(FileName), GetFileExInfoStandard, @fad) then
    RaiseLastOSError;
  Int64Rec(Result).Lo := fad.nFileSizeLow;
  Int64Rec(Result).Hi := fad.nFileSizeHigh;
end;

Or, if you prefer to return -1 in case of error you would write it like this:

function GetFileSize3(const FileName: string): Int64;
var
  fad: TWin32FileAttributeData;
begin
  if not GetFileAttributesEx(PChar(FileName), GetFileExInfoStandard, @fad) then
    exit(-1);
  Int64Rec(Result).Lo := fad.nFileSizeLow;
  Int64Rec(Result).Hi := fad.nFileSizeHigh;
end;

Some how this feels more natural than calling FindFirstFile, but that's perhaps just personal preference. There's really nothing wrong with the FindFirstFile approach. Although it doesn't need that iTmp variable. You can write it more clearly like this:

function GetFileSize1(const FileName: TFileName): Int64;
var
 SearchRec: TSearchRec;
begin
  if FindFirst(FileName, faAnyFile, SearchRec) = 0 then
  begin
    Result := SearchRec.Size;
    System.SysUtils.FindClose(SearchRec);
  end
  else
    Result := -1;
end;

Update: @CodeInChaos makes a good point about the approaches that don't open a file handle. These approaches can give inaccurate results for hard linked files.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Hello @David, this third solution raise only when file not is found then? I mean, is free-problem respect to first and second solution? Is correct? – Marcello Impastato Nov 17 '12 at 13:49
  • 1
    @Marcello I don't quite understand that comment. However, I've added more to the answer to cover error handling. And note well that your two functions handle errors in different ways. – David Heffernan Nov 17 '12 at 13:50
  • 1
    I'd guess that `GetFileSize1` needs to open a handle to the directory. I also wouldn't be surprised if it returned false information when used with hard linked files. – CodesInChaos Nov 17 '12 at 14:19
  • @CodesInChaos Yes, that's a good point about hard links. I don't know anything about opening handles to directories. – David Heffernan Nov 17 '12 at 14:27
  • `FindFirst` faster than a call to `CreateFile` win32 api? – Warren P Nov 18 '12 at 04:28
  • Sadly, I see it all the time in Delphi codebases. I have been using something like your GetFileSize3 for some time now, and I like it. I think that's the way to go for sure. – Warren P Nov 18 '12 at 21:05
2

The difference is, that GetFileSize1 reads the Meta Information of a file (Windows-API-Call) and GetFileSize2 has a direct touch to the file (getting FileHandle, walk through until the end to calculate the size).

So GetFileSize1 consumes less performance/ressources than GetFileSize2

UPDATE

I forgot to mention, if the file is already in use, you maybe not allowed to get access with a TFileStream, but the Meta Information will be available.

UPDATE (just another Variant of Davids suggestion)

function GetFileSize1(const FileName: TFileName): Int64;
var
 SearchRec: TSearchRec;
begin
  if FindFirst( FileName, faAnyFile, SearchRec ) = 0 then
    try
      Exit( SearchRec.Size );
    finally
      System.SysUtils.FindClose(SearchRec);
    end;
  Result := -1;
end;
Sir Rufo
  • 18,395
  • 2
  • 39
  • 73
  • Personally I like the version that doesn't do FindFirst OR opening a file, the one David shows as `GetFileSize3` – Warren P Nov 18 '12 at 04:29
  • @WarrenP so do i and thats why i upvoted davids answer a couple of seconds after posting :o) – Sir Rufo Nov 18 '12 at 04:33