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;
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?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With