Что такое "Время изменения"?

Вопросы программирования и использования среды Lazarus.

Модератор: Модераторы

Что такое "Время изменения"?

Сообщение Alex333 » 02.12.2011 21:22:22

Вопрос видимо не совсем по Lazarus (а скорее по WinAPI). Но столкнулся с этим, когда писал на Lazarus, поэтому пишу сюда.
Как следует из HELPа Windows SDK (в составе Delphi), в системе NTFS у файла (или каталога) есть 3 даты: создания, последней модификации, и последнего доступа. Соответственно, для их установки / чтения есть функции:

The SetFileTime function sets the date and time that a file was created, last accessed, or last modified.
BOOL SetFileTime(
HANDLE hFile, // identifies the file
CONST FILETIME *lpCreationTime, // time the file was created
CONST FILETIME *lpLastAccessTime, // time the file was last accessed
CONST FILETIME *lpLastWriteTime // time the file was last written
);

The GetFileTime function retrieves the date and time that a file was created, last accessed, and last modified.
BOOL GetFileTime(
HANDLE hFile, // identifies the file
LPFILETIME lpCreationTime, // address of creation time
LPFILETIME lpLastAccessTime, // address of last access time
LPFILETIME lpLastWriteTime // address of last write time
);

А теперь берём FAR (ну или Total, или кому ещё что нравится), выбираем файл и жмём Ctrl+A (для FAR). И видим ещё и четвёртый параметр - "Время изменения". Так вот мне интересно, а что это вообще такое, и как его прочитать / записать?
Alex333
новенький
 
Сообщения: 32
Зарегистрирован: 21.08.2011 19:14:28

Re: Что такое "Время изменения"?

Сообщение Alexx2000 » 02.12.2011 21:37:11

"Время изменения" это и есть LastWriteTime
Аватара пользователя
Alexx2000
постоялец
 
Сообщения: 489
Зарегистрирован: 25.10.2006 00:22:07
Откуда: Мытищи

Re: Что такое "Время изменения"?

Сообщение Alex333 » 02.12.2011 21:47:51

Я тоже так сначала думал. А потом взял всё по тому же Ctrl+A в FAR установил для них разные значения. И они спокойненько установились :shock:
Открываю картинку снова - установлены так, как я им велел :)
Alex333
новенький
 
Сообщения: 32
Зарегистрирован: 21.08.2011 19:14:28

Re: Что такое "Время изменения"?

Сообщение Alexx2000 » 02.12.2011 22:03:29

А Far какой версии? В Far2 только 3 поля как и положено.
Аватара пользователя
Alexx2000
постоялец
 
Сообщения: 489
Зарегистрирован: 25.10.2006 00:22:07
Откуда: Мытищи

Re: Что такое "Время изменения"?

Сообщение Alex333 » 02.12.2011 22:04:08

Скажу больше - когда я меняю, например, атрибут ReadOnly для файла, то LastWriteTime у него остается тем же, а вот это самое загадочное время изменения становится равным текущему (когда я поменял атрибут).

Добавлено спустя 1 минуту 51 секунду:
У меня FAR 2.0.1807
Скачайте с http://www.farmanager.com/download.php?l=ru если что.

Хотел приаттачить картинку, но чего-то ругается мне сервак на "Мало места..." или что-то такое...
Alex333
новенький
 
Сообщения: 32
Зарегистрирован: 21.08.2011 19:14:28

Re: Что такое "Время изменения"?

Сообщение Alexx2000 » 02.12.2011 22:15:21

Судя по исходникам Far, для его установки используется функция NtSetInformationFile
Аватара пользователя
Alexx2000
постоялец
 
Сообщения: 489
Зарегистрирован: 25.10.2006 00:22:07
Откуда: Мытищи

Re: Что такое "Время изменения"?

Сообщение Alex333 » 02.12.2011 22:16:02

╔══════════════════════════ Атрибуты ══════════════════════════╗
║ Изменить файловые атрибуты ║
║ a.bat ║
╟──────────────────────────────────────────────────────────────╢
║ Владелец: CLIENTXP1Alex ║
╟──────────────────────────────────────────────────────────────╢
║ [x] Только для чтения [x] Неиндексируемый ║
║ [x] Архивный [ ] Разреженный ║
║ [ ] Скрытый [ ] Временный ║
║ [ ] Системный [ ] Автономный ║
║ [ ] Сжатый [ ] Точка повторной обработки ║
║ [ ] Зашифрованный [ ] Виртуальный ║
╟──────────────────────────────────────────────────────────────╢
║ ДД.ММ.ГГГГГ чч:мм:сс,мс ║
║ Время последней записи: 01.12.2011 21:45:22,812 ║
║ Время создания: 19.09.2011 18:38:19,468 ║
║ Время последнего доступа: 02.12.2011 22:00:02,890 ║
║ Время изменения: 02.12.2011 22:01:59,875 ║
║ [ Исходное ] [ Текущее ] [ Сброс ] ║
╟──────────────────────────────────────────────────────────────╢
║ { Установить } [ Системные свойства ] [ Отмена ] ║
╚══════════════════════════════════════════════════════════════╝

Ну, вот текстовый буфер скопировал вместо картинки. Кривовато, но понятно д.б.

Добавлено спустя 10 минут 32 секунды:
Мда... но судя по http://hex.pp.ua/nt/NtSetInformationFile.php , она ещё и недокументированная. И что-то в описании для FILE_INFORMATION_CLASS
не наблюдается, что ей надо указывать для даты.
Ладно - пусть Microsoft оставит себе свои хакерские недокументированные штучки. Обойдусь тремя временами.
Alex333
новенький
 
Сообщения: 32
Зарегистрирован: 21.08.2011 19:14:28

Re: Что такое "Время изменения"?

Сообщение Alexx2000 » 02.12.2011 22:30:12

Вроде документированная: http://msdn.microsoft.com/en-us/library ... 85%29.aspx и передаваемая структура http://msdn.microsoft.com/en-us/library ... 85%29.aspx
Аватара пользователя
Alexx2000
постоялец
 
Сообщения: 489
Зарегистрирован: 25.10.2006 00:22:07
Откуда: Мытищи

Re: Что такое "Время изменения"?

Сообщение Alex333 » 03.12.2011 00:22:57

Наваял на скорую руку небольшой тест (на Delphi пока что):
Код: Выделить всё
type
  IO_STATUS_BLOCK = Record
    NTSTATUS:Pointer;
    Information:Pointer;
  end;
  PIO_STATUS_BLOCK = ^IO_STATUS_BLOCK;

  FILE_BASIC_INFORMATION = Record
    CreationTime:FileTime;
    LastAccessTime:FileTime;
    LastWriteTime:FileTime;
    ChangeTime:FileTime;
    FileAttributes:ULong;
  end;
  PFILE_BASIC_INFORMATION = ^FILE_BASIC_INFORMATION;

  const FileBasicInformation = 4;
        STATUS_SUCCESS = 0;

function NtSetInformationFile(hFile: THandle;IoStatusBlock:PIO_STATUS_BLOCK;
  FileInformation:PFILE_BASIC_INFORMATION;Length:ULong;FileInformationClass:ULong):
  ULong;stdcall;external 'ntdll' name 'NtSetInformationFile';

procedure Test(const FileName:String);
var sr:TSearchRec;
    fd:THandle;
    StatusBlock:IO_STATUS_BLOCK;
    FileInformation:FILE_BASIC_INFORMATION;
    r:ULong;
    errPlace:Integer;
begin
  errPlace:=0;
  try
    try
      if FindFirst(FileName,faAnyFile,sr) <> 0 then begin
         errPlace:=1;
         RaiseLastOSError();
      end;
      FileInformation.CreationTime:=sr.FindData.ftCreationTime;
      FileInformation.LastAccessTime:=sr.FindData.ftLastAccessTime;
      FileInformation.LastWriteTime:=sr.FindData.ftLastWriteTime;
      //
      FileInformation.ChangeTime:=FileInformation.LastWriteTime;
      FileInformation.ChangeTime.dwHighDateTime:=FileInformation.ChangeTime.dwHighDateTime+100;
      //
      fd:=CreateFile(Pchar(FileName),GENERIC_WRITE,FILE_SHARE_WRITE,Nil,OPEN_EXISTING,
        FILE_FLAG_BACKUP_SEMANTICS,0); 
      if fd = INVALID_HANDLE_VALUE then begin
        errPlace:=2;
        RaiseLastOSError();
      end;
      try
        r:=NtSetInformationFile(fd,@StatusBlock,@FileInformation,sizeof(FileInformation),
        FileBasicInformation);
        if r<>STATUS_SUCCESS then begin
          errPlace:=3;
          RaiseLastOSError();
        end;
      finally
        CloseHandle(fd);
      end;
    except
      on e:Exception do begin
        Raise Exception.Create('Ошибка в позиции '+inttostr(errPlace)+': '+e.Message);
      end;
    end;
  finally
    FindClose(sr);
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  try
    Test('e:a.bat');
    showmessage('Test Ok!');
  except
    on e:Exception do begin
      showmessage(e.Message);
    end;
  end;
end;


Как и ожидал, с первого раза не заработало. Выдается
---------------------------
Project1
---------------------------
Ошибка в позиции 3: A call to an OS function failed
---------------------------
OK
---------------------------
т.е. ошибка, а какая - не понятно.

Подскажите, где накосячил?
А ещё интересно - описание какого-нибудь аналога для чтения (типа NtSetInformationFile() ) я что-то не нашёл...

Добавлено спустя 6 минут 9 секунд:
в смысле NtGetInformationFile() не нашёл
Alex333
новенький
 
Сообщения: 32
Зарегистрирован: 21.08.2011 19:14:28

Re: Что такое "Время изменения"?

Сообщение Alexx2000 » 03.12.2011 11:48:34

Alex333 писал(а):Как и ожидал, с первого раза не заработало. Выдается
---------------------------
Project1
---------------------------
Ошибка в позиции 3: A call to an OS function failed
---------------------------
OK
---------------------------
т.е. ошибка, а какая - не понятно.

Подскажите, где накосячил?


Судя по всему размер буфера слишком маленький, вот рабочий пример:
Код: Выделить всё
function NtSetInformationFile(hFile: THandle;IoStatusBlock:PIO_STATUS_BLOCK;
  FileInformation:PFILE_BASIC_INFORMATION;Length:ULong;FileInformationClass:ULong):
  ULong;stdcall;external 'ntdll' name 'NtSetInformationFile';

function RtlNtStatusToDosError( Status: ULong): ULONG;stdcall;external 'ntdll' name 'RtlNtStatusToDosError';

procedure Test(const FileName:String);
var sr:TSearchRec;
    fd:THandle;
    StatusBlock:IO_STATUS_BLOCK;
    Buffer: array[0..1023] of Byte;
    FileInformation:FILE_BASIC_INFORMATION absolute Buffer;
    r:ULong;
    errPlace:Integer;
begin
  errPlace:=0;
  try
    try
      if FindFirst(FileName,faAnyFile,sr) <> 0 then begin
         errPlace:=1;
         RaiseLastOSError();
      end;
      FileInformation.CreationTime:=sr.FindData.ftCreationTime;
      FileInformation.LastAccessTime:=sr.FindData.ftLastAccessTime;
      FileInformation.LastWriteTime:=sr.FindData.ftLastWriteTime;
      //
      FileInformation.ChangeTime:=FileInformation.LastWriteTime;
      FileInformation.ChangeTime.dwHighDateTime:=FileInformation.ChangeTime.dwHighDateTime+100;
      //
      fd:=CreateFile(Pchar(FileName),GENERIC_WRITE,FILE_SHARE_WRITE,Nil,OPEN_EXISTING,
        FILE_FLAG_BACKUP_SEMANTICS,0);
      if fd = INVALID_HANDLE_VALUE then begin
        errPlace:=2;
        RaiseLastOSError();
      end;
      try
        r:=NtSetInformationFile(fd,@StatusBlock,@Buffer,1024,
        FileBasicInformation);
        if r<>STATUS_SUCCESS then begin
          errPlace:=3;
        SetLastError(RtlNtStatusToDosError(r));
          RaiseLastOSError();
        end;
      finally
        CloseHandle(fd);
      end;
    except
      on e:Exception do begin
        Raise Exception.Create('Îøèáêà â ïîçèöèè '+inttostr(errPlace)+': '+e.Message);
      end;
    end;
  finally
    FindClose(sr);
  end;
end;

Alex333 писал(а):А ещё интересно - описание какого-нибудь аналога для чтения (типа NtSetInformationFile() ) я что-то не нашёл...

Добавлено спустя 6 минут 9 секунд:
в смысле NtGetInformationFile() не нашёл

Для чтения используется функция NtQueryInformationFile
Аватара пользователя
Alexx2000
постоялец
 
Сообщения: 489
Зарегистрирован: 25.10.2006 00:22:07
Откуда: Мытищи

Re: Что такое "Время изменения"?

Сообщение Alex333 » 03.12.2011 20:12:35

Да, это работает. Буфер я делать большой не стал (странно, что не знал такую штуку, как absolute. Это получается что-то типа C-шного UNION что ли? )
А добавил 4 байта в структуру
FILE_BASIC_INFORMATION = packed record
CreationTime:FileTime;
LastAccessTime:FileTime;
LastWriteTime:FileTime;
ChangeTime:FileTime;
FileAttributes:ULong;
Reserve:ULong; //Без этого ругается на маленький буфер. Странно, по описанию бы не должен.
end;
Такое ощущение, что ему надо 8 байт под эти атрибуты, а не 4. Ну, в общем, так работает.
Ну и пришлось ещё сделать забытое в тот раз
FileInformation.FileAttributes:=sr.Attr;
FileInformation.Reserve:=0; //От греха подальше...
иначе атрибуты получались случайными, и иногда такими, что была ошибка.
А можно было бы просто 0 написать, т.к. "Setting any member of the structure to zero tells ZwSetInformationFile to leave the current information about the file for that member unchanged"

Осталось только вместо тестовой процедуры написать нормальную - понимающую TDateTime вместо FileTime и т.д. Ну, это уже орешки.
Спасибо.

И про RtlNtStatusToDosError тоже бы я сам не сразу догадался.

Ну, что ж - причешу эту функцию, возьмусь за NtQueryInformationFile. С первого раза тоже не пошла. Если не получится - уж не обессудьте, снова к Вам :-)

Добавлено спустя 2 часа 9 минут 27 секунд:
Вот, что-то наваял относительно законченное. Кому надо - пользуйтесь, кто найдёт ошибки - ткните меня в них.
Код: Выделить всё
function FileTimeToDateTime(FileTime: TFileTime): TDateTime;
var
  ModifiedTime: TFileTime;
  SystemTime: TSystemTime;
begin
  Result := 0;
  if (FileTime.dwLowDateTime = 0) and (FileTime.dwHighDateTime = 0) then
    Exit;
  try
    FileTimeToLocalFileTime(FileTime, ModifiedTime);
    FileTimeToSystemTime(ModifiedTime, SystemTime);
    Result := SystemTimeToDateTime(SystemTime);
  except
    Result := Now;  // Хоть что-нибудь вернуть, если ошибка
end;
end;

function DateTimeToFileTime(FileTime: TDateTime): TFileTime;
var
  LocalFileTime, Ft: TFileTime;
  SystemTime: TSystemTime;
begin
  Result.dwLowDateTime := 0;
  Result.dwHighDateTime := 0;
  DateTimeToSystemTime(FileTime, SystemTime);
  SystemTimeToFileTime(SystemTime, LocalFileTime);
  LocalFileTimeToFileTime(LocalFileTime, Ft);
  Result := Ft;
end;

//Описалово тут:
//http://msdn.microsoft.com/en-us/library/windows/hardware/ff557671%28v=vs.85%29.aspx

type
  NT_STATUS = Cardinal;

  IO_STATUS_BLOCK = packed record
    NTSTATUS:Pointer;
    Information: DWORD;
  end;
  PIO_STATUS_BLOCK = ^IO_STATUS_BLOCK;

  FILE_BASIC_INFORMATION = packed record
    CreationTime:FileTime;
    LastAccessTime:FileTime;
    LastWriteTime:FileTime;
    ChangeTime:FileTime;
    FileAttributes:ULong;
    Reserve:ULong;  //Без этого ругается на маленький буфер. Странно...
  end;
  PFILE_BASIC_INFORMATION = ^FILE_BASIC_INFORMATION;

  const FileBasicInformation = 4;
        STATUS_SUCCESS = 0;

function RtlNtStatusToDosError(Status:NT_STATUS):ULONG;stdcall;external 'ntdll' name 'RtlNtStatusToDosError';

function NtSetInformationFile(hFile: THandle;IoStatusBlock:PIO_STATUS_BLOCK;
  FileInformation:PFILE_BASIC_INFORMATION;Length:ULong;FileInformationClass:ULong):
  NT_STATUS;stdcall;external 'ntdll' name 'NtSetInformationFile';

function NtQueryInformationFile(hFile:THandle;IoStatusBlock:PIO_STATUS_BLOCK;
  FileInformation:PFILE_BASIC_INFORMATION;Length:ULong;FileInformationClass:ULong):
  ULong;stdcall;external 'ntdll' name 'ZwQueryInformationFile';

//Чтение времён и атрибутов файла (или каталога, который с т.з. ОС тоже файл)
//  FileName - имя файла или каталога
//  ErrMessage - буфер для дополнительного текста ошибки
//  CreationTime,LastWriteTime,LastAccessTime,ChangeTime - 4 возвращаемые даты
//  FileAttributes - набор атрибутов (битовая маска)
//Выход: true/false
//Если result = False, проверять GetLastError или вызывать RaiseLastOSError(),чтобы понять причину
function GetFileDatesAndAttr(const FileName:String;out ErrMessage:String;
  out CreationTime,LastWriteTime,LastAccessTime,ChangeTime:TDateTime;
  out FileAttributes:Integer):Boolean;
var
  fd:THandle;
  StatusBlock:IO_STATUS_BLOCK;
  FileInformation:FILE_BASIC_INFORMATION;
  r:ULong;
begin
  Result := False;SetLastError(0);ErrMessage:='';
  fd:=CreateFile(Pchar(FileName),GENERIC_READ,FILE_SHARE_READ,Nil,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,0);
  if fd = INVALID_HANDLE_VALUE then begin
    ErrMessage := 'Ошибка при открытии файла "'+FileName+'"';
  end
  else begin
    try
      r:=NtQueryInformationFile(fd,@StatusBlock,@FileInformation,sizeof(FileInformation),FileBasicInformation);
      if r = STATUS_SUCCESS then begin
        CreationTime:=FileTimeToDateTime(FileInformation.CreationTime);
        LastAccessTime:=FileTimeToDateTime(FileInformation.LastAccessTime);
        LastWriteTime:=FileTimeToDateTime(FileInformation.LastWriteTime);
        ChangeTime:=FileTimeToDateTime(FileInformation.ChangeTime);
        Result:=True;
      end
      else begin
        ErrMessage := 'Ошибка при вызове NtQueryInformationFile()';
        SetLastError(RtlNtStatusToDosError(r));
      end;
    finally
      CloseHandle(fd);
    end;
  end;
end;

//Запись времён и атрибутов файла (или каталога, который с т.з. ОС тоже файл)
//  FileName - имя файла или каталога
//  ErrMessage - буфер для дополнительного текста ошибки
//  CreationTime,LastWriteTime,LastAccessTime,ChangeTime - 4 устанавливаемые даты
//  FileAttributes - набор устанавливаемых атрибутов (битовая маска)
//Если какие-то параметры менять не нужно, передать значение 0.
//Выход: true/false
//Если result = False, проверять GetLastError или вызывать RaiseLastOSError(),чтобы понять причину
function SetFileDatesAndAttr(const FileName:String;out ErrMessage:String;
  CreationTime:TDateTime=0;LastWriteTime:TDateTime=0;LastAccessTime:TDateTime=0;
  ChangeTime:TDateTime=0;FileAttributes:Integer=0):Boolean;
var
  fd:THandle;
  StatusBlock:IO_STATUS_BLOCK;
  FileInformation:FILE_BASIC_INFORMATION;
  r:ULong;
  procedure SetFITime(DTTime:TDateTime;out Ft:TFileTime);
  begin
    if DTTime = 0 then begin
      ft.dwHighDateTime:=0;
      ft.dwLowDateTime:=0;
    end
    else begin
      ft:=DateTimeToFileTime(DTTime);
    end;
  end;
begin
  Result := False;SetLastError(0);ErrMessage:='';
  fd:=CreateFile(Pchar(FileName),GENERIC_WRITE,FILE_SHARE_WRITE,Nil,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,0);
  if fd = INVALID_HANDLE_VALUE then begin
    ErrMessage := 'Ошибка при открытии файла "'+FileName+'"';
  end
  else begin
    try
      SetFITime(CreationTime,FileInformation.CreationTime);
      SetFITime(LastWriteTime,FileInformation.LastWriteTime);
      SetFITime(LastAccessTime,FileInformation.LastAccessTime);
      SetFITime(ChangeTime,FileInformation.ChangeTime);
      FileInformation.FileAttributes:=FileAttributes;
      FileInformation.Reserve:=0;  //От греха подальше...
      r:=NtSetInformationFile(fd,@StatusBlock,@FileInformation,sizeof(FileInformation),FileBasicInformation);
      if r = STATUS_SUCCESS then begin
        Result:=True;
      end
      else begin
        ErrMessage := 'Ошибка при вызове NtQueryInformationFile()';
        SetLastError(RtlNtStatusToDosError(r));
      end;
    finally
      CloseHandle(fd);
    end;
  end;
end;

procedure TForm1.Button3Click(Sender: TObject);
var AddErr:String;
    dtCreate,dtModify,dtAcc,dtChg:TDateTime;
    Attr:Integer;
    s:String;
begin
  s:='c:\98';AddErr:='Возникло исключение: ';
  try
    if not SetFileDatesAndAttr(s,AddErr,StrToDateTime('01.01.2011'),
      StrToDateTime('01.02.2011'),StrToDateTime('01.03.2011'),
      StrToDateTime('01.04.2011'),0) then
      RaiseLastOSError();
    if not GetFileDatesAndAttr(s,AddErr,dtCreate,dtModify,dtAcc,dtChg,Attr) then
      RaiseLastOSError();
    showmessage('Установленные даты для "'+s+'"'#13#10+
    DateTimeToStr(dtCreate)+' '+DateTimeToStr(dtModify)+' '+
    DateTimeToStr(dtAcc)+' '+DateTimeToStr(dtChg));
  except
    on e:Exception do
      ShowMessage(AddErr+': '#13#10+e.Message);
  end;
end;


---------------------------
Project1
---------------------------
Установленные даты для "c:\98"
01.01.2011 01.02.2011 01.03.2011 01.04.2011
---------------------------
OK
---------------------------
Alex333
новенький
 
Сообщения: 32
Зарегистрирован: 21.08.2011 19:14:28


Вернуться в Lazarus

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 231

Рейтинг@Mail.ru