Подтвердить что ты не робот

Проблема с 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;
4b9b3361

Ответ 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;