Как сделать калькулятор в Delphi?

Delphi - объектно-ориентированный язык программирования, разработанный компанией Borland в 1995 году. Он основан на языке программирования Pascal, но имеет более расширенные возможности и добавлены новые функции.

Как Delphi реализует многоплатформенную разработку?

Delphi является интегрированной средой разработки (IDE), которая позволяет разрабатывать программное обеспечение для различных платформ, включая Windows, macOS, Android и iOS. Delphi достигает многоплатформенности с помощью...

Следить за изменениями в каталоге

Советы » Каталоги » Следить за изменениями в каталоге

Очень хорошая функция, только " Windows 95/98/Me: Unsupported" . Т.е. в Win9x этой функции нет. Но мы же пишем для NT, а там с этой функцией все в порядке (если 3-ий сервис пак для NT3.1 поставили :)))

Кратко пройдемся по описанию функции (взято из windows

function

ReadDirectoryChangesW( hDirectory: THandle; // описатель каталога, за которым надо следить lpBuffer: Pointer; // Указатель на буфер, в который будет записана информация nBufferLength: DWORD; // Размер буфера bWatchSubtree: Bool; // Следить ли за подкаталогами dwNotifyFilter: DWORD; // Фильтр действий lpBytesReturned: LPDWORD; // Сколько было записано в буфер lpOverlapped: POverlapped; // Для асинхронной работы lpCompletionRoutine: FARPROC // Функция, которая будет вызвана при окончании операции ): BOOL; stdcall

;

Ну а теперь пример работы этой функции (исходник этого примера (пока без комментариев!! и на Delphi6) можно скачать здесь)

Чтобы программа могла нормально работать во время ожидания очередного изменения, мы функции мониторинга выделим отдельный поток. Поток " сделан" на WinAPI (функция WorkThread). При нажатии на одну кнопку он будет создаваться, а на другую - жестоко уничтожаться. Вся полезная информация будет выводиться в TListView.

Функция потока будет описана так:

procedure

WorkThread(LV: TListView); stdcall

;

LV - это то, во что мы будем выводить инфу. И не забывайте stdcall;

А вот ее текст:

procedure

WorkThread(LV : TListView);stdcall

; var

hDir : THandle; lpBuf : Pointer; Ptr : Pointer; cbReturn : Cardinal; FileName : PWideChar; Item : TListItem; sTime : _SYSTEMTIME; begin

// Сначала нам надо получить описатель каталога, за которым мы будем следить // В данном примере это будет весь диск C: hDir := CreateFile ('C:',GENERIC_READ,FILE_SHARE_READ or

FILE_SHARE_WRITE or

FILE_SHARE_DELETE,nil

,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,0); // Если ошиблись... if

hDir = INVALID_HANDLE_VALUE then

begin

ShowMessage(SysErrorMessage(GetLastError)); exit; end

; // Выделяем память под буфер // const BUF_SIZE = 2048 - думаю вполне достаточно GetMem(lpBuf,BUF_SIZE); repeat

// очищаем память перед записью в нее (на всякий случай) ZeroMemory(lpBuf,BUF_SIZE); // Теперь мы будем ждать пока чего-нибудь в интересующем нас каталоге // изменится или произойдет ошибка (и мы выйдем из цикла) // FILE_NOTIFY_CHANGE - это список флагов - о них ниже. if

not

ReadDirectoryChangesW(hDir,lpBuf,BUF_SIZE,true

, FILE_NOTIFY_CHANGE,@cbReturn,nil

,nil

) then

Break; // Сюда мы попадаем, если функция выполнилась успешно // и lpBuf указывает на одну или несколько структур FILE_NOTIFY_INFORMATION Ptr:=lpBuf;

Отойдем пока от исходного кода и рассмотрим, что у нас появится в буфере. В данный момент lpBuf и Ptr указывают на первую структуру FILE_NOTIFY_INFORMATION. Вторым полем этой структуры является - Action -тип действия, которое было совершено. Четвертым - FileName - первый символ имени файла. Имя файла не заканчивается нулем #0 и для определения его длины используется 3 параметр - FileNameLength. При этом надо учесть, что имя файла в формате Unicode т.е. каждый символ занимает 2 байта, а FileNameLength дается в байтах. Придется эту длину делить на 2, чтобы узнать кол-во символов.

Но возникает вопрос - как узнать, сколько таких структур было записано в буфер. Для этого используется 1 параметр структуры - NextEntryOffset. Если он не равен нулю, то в нем будет кол-во байт, через которые находится следующая запись и нам надо сдвинуть указатель на это кол-во байт, чтобы " получить" следующую структуру. И так далее, пока NextEntryOffset не будет равен 0 (т.е. эта запись была последней).

  repeat

// Добавляем новый элемент в TListView (ViewStyle = vsReport ) Item := LV.Items.Add; // Выделяем память под имя файла GetMem(FileName,PFileNotifyInformation(Ptr).FileNameLength+2); // Очищаем память - чтобы последним символом после копирования // был бы #0 нуль ZeroMemory(FileName,PFileNotifyInformation(Ptr).FileNameLength+2); // WinAPI функция для копирования Unicode строки lstrcpynW(FileName,PFileNotifyInformation(Ptr).FileName, PFileNotifyInformation(Ptr).FileNameLength div

2+1); // Имя файла у нас дается относительно папки // т.е.если изменится файл C:File est.dat, то FileName // будет равно File est.dat Item.Caption:='C:'+FileName; // Имя файла нам больше не нужно - очищаем память FreeMem(FileName); // Определяем тип произошедшего действия case

PFileNotifyInformation(Ptr).Action of

FILE_ACTION_ADDED : Item.SubItems.Add('Файл был создан'); FILE_ACTION_REMOVED : Item.SubItems.Add('Файл был удален'); FILE_ACTION_MODIFIED : Item.SubItems.Add('Файл был изменен'); FILE_ACTION_RENAMED_OLD_NAME : Item.SubItems.Add('Файл был переименован и в имени файла - предыдущее имя'); FILE_ACTION_RENAMED_NEW_NAME : Item.SubItems.Add('новое имя после переименования'); else

Item.SubItems.Add('Произошло что-то странное'); end

; // Время, когда произошло событие GetLocalTime(sTime); with

sTime do

Item.SubItems.Add(Format('%.2d:%.2d:%.2d',[wHour,wMinute,wSecond])); // 13:54:20 // Если эта запись не последняя (NextEntryOffset < > 0), то... if

PFileNotifyInformation(Ptr).NextEntryOffset=0 then

Break else

begin

// ... добавляем строку в примечания (если интересно посмотреть смещение) Item.SubItems.Add('Offset : '+ IntToStr(PFileNotifyInformation(Ptr).NextEntryOffset)); //Передвигаем указатель на NextEntryOffset байт вперед Inc(Cardinal(Ptr),PFileNotifyInformation(Ptr).NextEntryOffset); // Теперь Ptr указывает на следующую запись end

; // Передвигать надо именно Ptr, а не lpBuf until

false

; until

false

; // Очищаем память FreeMem(lpBuf); end

;

Параметр функции dwNotifyFilter - действия, информацию о которых мы хотим получать.

  • FILE_NOTIFY_CHANGE_FILE_NAME - создание, удаление, переименование файла.
  • FILE_NOTIFY_CHANGE_DIR_NAME - созданием и удаление каталога.
  • FILE_NOTIFY_CHANGE_ATTRIBUTES - изменение атрибутов файла.
  • FILE_NOTIFY_CHANGE_SIZE - изменение размера файла.
  • FILE_NOTIFY_CHANGE_LAST_WRITE - изменение файла (это и предыдущее уведомление будет получено только в момент реальной записи файла на диск).
  • FILE_NOTIFY_CHANGE_LAST_ACCESS - изменение времени последнего доступа.
  • FILE_NOTIFY_CHANGE_CREATION - изменение времени создания файла.
  • FILE_NOTIFY_CHANGE_SECURITY - изменение параметров безопасности (прав доступа и т.д.)

У меня в примере используются FILE_NOTIFY_CHANGE_FILE_NAME, FILE_NOTIFY_CHANGE_DIR_NAME и FILE_NOTIFY_CHANGE_LAST_WRITE.

А теперь надо только запустить поток.

procedure

TForm1.Button1Click(Sender: TObject); var

ThID : Cardinal; begin

// hThread - THandle - глобальная переменная // Создаем поток // LV - TListView, WorkThread - функция выше hThread:=CreateThread(nil

,0,@WorkThread,LV,0,ThID); // В случае неудачи выводим сообщение if

hThread=0 then

ShowMessage(SysErrorMessage(GetLastError)); end

;

У меня в исходниках поток останавливается функцией TerminateThread(hThread,Cardinal(-1)). Но при таком завершении не будут освобождены все ресурсы, занятые потоком (а это как минимум BUF_SIZE байт памяти. Вместо этой функции было бы лучше использовать SuspendThread(hThread), а при запуске проверять на существование потока WaitForSingleObject(hThread,0)= WAIT_TIMEOUT и если он существует - делать ResumeThread(hThread)... но в исходниках этого пока нет :)

Другое по теме:

Категории

Статьи

Советы

Copyright © 2024 - All Rights Reserved - www.delphirus.com