Проблема с FileExists и измененной датой
На моем сервере есть несколько файлов с измененной датой 31/DEC/1979 (не спрашивайте меня почему). Поэтому FileExists
возвращает false.
Sysutils.FileExists
выглядит следующим образом:
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;
Мой вопрос: почему функция зависит от FileAge
в первую очередь?
Не достаточна ли следующая строка:
if (FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then
// Yes the file exists!
Или даже на основе атрибутов файлов:
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;
Ответы
Ответ 1
Современные версии Delphi реализуют FileExists
в целом так же, как и ваш код. Реализация имеет дополнительную обработку символических ссылок, но в остальном по существу идентична вашей версии.
Есть один интересный нюанс в современной реализации Delphi. Если вызов GetFileAttributes
возвращает INVALID_FILE_ATTRIBUTES
, тогда код не сразу выйдет из строя. Вместо этого он делает это:
LastError := GetLastError;
Result := (LastError <> ERROR_FILE_NOT_FOUND) and
(LastError <> ERROR_PATH_NOT_FOUND) and
(LastError <> ERROR_INVALID_NAME) and ExistsLockedOrShared(Filename);
И реализация ExistsLockedOrShared
использует FindFirstFile
и проверку FILE_ATTRIBUTE_DIRECTORY
на dwFileAttributes
. Это означает, что GetFileAttributes
может выйти из строя, если файл существует, но заблокирован. Но этот FindFirstFile
может преуспеть в таком сценарии. Это разумно, потому что FindFirstFile
использует метаданные файла, а не данные, хранящиеся в самом файле.
Трудно сказать, почему код такой, как в старых версиях. Я считаю это слабым. Лично я бы заменил FileExists
на лучшую версию, используя крючок кода. Например: Патч рутинный вызов в delphi
Как всегда, есть статья Raymond Chen по теме: Суеверие: почему GetFileAttributes как работает тестовый файл old-timers?
Ответ 2
Судя по "современной" реализации FileExists
(которая не использует FileAge
, а также оптимизирована и может следовать символическим ссылкам, чтобы проверить, существуют ли связанные файлы):
- первый вариант (
(FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0
) в порядке;
- второй вариант (
GetFileAttributes
) может завершиться неудачно, если файл заблокирован или разделен.
Ответ 3
В старой версии Delphi вы можете загрузить Jedi Code Library. Он имеет следующую реализацию (помимо многих других полезных классов и функций):
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;