Что такое "Время изменения"?
Модератор: Модераторы
Что такое "Время изменения"?
Вопрос видимо не совсем по 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). И видим ещё и четвёртый параметр - "Время изменения". Так вот мне интересно, а что это вообще такое, и как его прочитать / записать?
Как следует из 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). И видим ещё и четвёртый параметр - "Время изменения". Так вот мне интересно, а что это вообще такое, и как его прочитать / записать?
Я тоже так сначала думал. А потом взял всё по тому же Ctrl+A в FAR установил для них разные значения. И они спокойненько установились
Открываю картинку снова - установлены так, как я им велел
Открываю картинку снова - установлены так, как я им велел
Скажу больше - когда я меняю, например, атрибут ReadOnly для файла, то LastWriteTime у него остается тем же, а вот это самое загадочное время изменения становится равным текущему (когда я поменял атрибут).
Добавлено спустя 1 минуту 51 секунду:
У меня FAR 2.0.1807
Скачайте с http://www.farmanager.com/download.php?l=ru если что.
Хотел приаттачить картинку, но чего-то ругается мне сервак на "Мало места..." или что-то такое...
Добавлено спустя 1 минуту 51 секунду:
У меня FAR 2.0.1807
Скачайте с http://www.farmanager.com/download.php?l=ru если что.
Хотел приаттачить картинку, но чего-то ругается мне сервак на "Мало места..." или что-то такое...
╔══════════════════════════ Атрибуты ══════════════════════════╗
║ Изменить файловые атрибуты ║
║ 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 оставит себе свои хакерские недокументированные штучки. Обойдусь тремя временами.
║ Изменить файловые атрибуты ║
║ 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 оставит себе свои хакерские недокументированные штучки. Обойдусь тремя временами.
- Alexx2000
- постоялец
- Сообщения: 490
- Зарегистрирован: 25.10.2006 00:22:07
- Откуда: Мытищи
- Контактная информация:
Вроде документированная: http://msdn.microsoft.com/en-us/library ... 85%29.aspx и передаваемая структура http://msdn.microsoft.com/en-us/library ... 85%29.aspx
Наваял на скорую руку небольшой тест (на Delphi пока что):
Как и ожидал, с первого раза не заработало. Выдается
---------------------------
Project1
---------------------------
Ошибка в позиции 3: A call to an OS function failed
---------------------------
OK
---------------------------
т.е. ошибка, а какая - не понятно.
Подскажите, где накосячил?
А ещё интересно - описание какого-нибудь аналога для чтения (типа NtSetInformationFile() ) я что-то не нашёл...
Добавлено спустя 6 минут 9 секунд:
в смысле NtGetInformationFile() не нашёл
Код: Выделить всё
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() не нашёл
- Alexx2000
- постоялец
- Сообщения: 490
- Зарегистрирован: 25.10.2006 00:22:07
- Откуда: Мытищи
- Контактная информация:
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
Да, это работает. Буфер я делать большой не стал (странно, что не знал такую штуку, как 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 секунд:
Вот, что-то наваял относительно законченное. Кому надо - пользуйтесь, кто найдёт ошибки - ткните меня в них.
---------------------------
Project1
---------------------------
Установленные даты для "c:\98"
01.01.2011 01.02.2011 01.03.2011 01.04.2011
---------------------------
OK
---------------------------
А добавил 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
---------------------------
