Reputation: 2520
On my server there are a few files with Modified Date 31/DEC/1979 (Don't ask me why). So FileExists
returns false.
Sysutils.FileExists
looks like this:
function FileAge(const FileName: string): Integer;
var
Handle: THandle;
FindData: TWin32FindData;
LocalFileTime: TFileTime;
begin
Handle := FindFirstFile(PChar(FileName), FindData);
if Handle <> INVALID_HANDLE_VALUE then
begin
Windows.FindClose(Handle);
if (FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then
begin
FileTimeToLocalFileTime(FindData.ftLastWriteTime, LocalFileTime);
if FileTimeToDosDateTime(LocalFileTime, LongRec(Result).Hi,
LongRec(Result).Lo) then Exit;
end;
end;
Result := -1;
end;
function FileExists(const FileName: string): Boolean;
begin
Result := FileAge(FileName) <> -1;
end;
My question is, Why does the function depends on FileAge
in the first place?
Isn't the following line sufficient?:
if (FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then
// Yes the file exists!
Or even based on file attributes:
function MyFileExists(const Name: string): Boolean;
var
R: DWORD;
begin
R := GetFileAttributes(PChar(Name));
Result := (R <> DWORD(-1)) and ((R and FILE_ATTRIBUTE_DIRECTORY) = 0);
end;
Upvotes: 12
Views: 3476
Reputation: 16045
For old Delphi versiosn you can download Jedi Code Library. It has the following implementation (aside of many other useful classes and functions):
function FileExists(const FileName: string): Boolean;
{$IFDEF MSWINDOWS}
var
Attr: Cardinal;
{$ENDIF MSWINDOWS}
begin
if FileName <> '' then
begin
{$IFDEF MSWINDOWS}
// FileGetSize is very slow, GetFileAttributes is much faster
Attr := GetFileAttributes(Pointer(Filename));
Result := (Attr <> $FFFFFFFF) and (Attr and FILE_ATTRIBUTE_DIRECTORY = 0);
{$ELSE ~MSWINDOWS}
// Attempt to access the file, doesn't matter how, using FileGetSize is as good as anything else.
Result := FileGetSize(FileName) <> -1;
{$ENDIF ~MSWINDOWS}
end
else
Result := False;
end;
Upvotes: 1
Reputation: 27493
Judging by the 'modern' implementation of FileExists
(which does not use FileAge
and also optimized and can follow symlinks to check if linked files exist):
(FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0
) is OK;GetFileAttributes
) could fail if file is locked or shared.Upvotes: 4
Reputation: 612954
The modern versions of Delphi implement FileExists
in broadly the same way as your code does. The implementation has extra handling for symlinks but is otherwise essentially identical to your version.
There's one interesting nuance in the modern Delphi implementation. If the call to GetFileAttributes
returns INVALID_FILE_ATTRIBUTES
, then the code doesn't immediately bail out. Instead it does this:
LastError := GetLastError;
Result := (LastError <> ERROR_FILE_NOT_FOUND) and
(LastError <> ERROR_PATH_NOT_FOUND) and
(LastError <> ERROR_INVALID_NAME) and ExistsLockedOrShared(Filename);
And the implementation of ExistsLockedOrShared
uses FindFirstFile
and a check of the FILE_ATTRIBUTE_DIRECTORY
on dwFileAttributes
. This indicates that GetFileAttributes
can fail when the file exists, but is locked. But that FindFirstFile
can succeed in such a scenario. That's reasonable because FindFirstFile
uses the file metadata rather than the data stored in the file itself.
It's hard to say why the code is is the way it is in the older versions. I think it's weak. Personally I would replace FileExists
with a better version, using a code hook. For example: Patch routine call in delphi
As always, there's a Raymond Chen article on the subject: Superstition: Why is GetFileAttributes the way old-timers test file existence?
Upvotes: 11