|
Навигация
|
Главная » Delphi Программируем в DelphiИсточник: webdelphi В предыдущем посте мы остановились на том. что разработали небольшое приложение, которое проводило мониторинг изменений в определенной директории и, в случае обнаружения какого-либо изменения, "сигналило" нам. Для организации мониторинга мы использовали поток (TThread) в котором использовалось три взаимосвязанные функции Windows: FindFirstChangeNotification , FindNextChangeNotification и FindCloseChangeNotification .Как говорилось ранее, с помощь этих функций нельзя узнать какую-либо специфическую информацию об изменениях. Так, например, при срабатывании события мы не могли узнать изменилось ли имя файла или был добавлен новый файл. Или, если произошла смена имени файла, то мы не можем узнать какое имя было до смены и какое стало после. Все эти нюансы могут натолкнуть неподготовленного разработчика на мысль, что использование приведенных выше функций ограничено - задача мониторинга изменений в директории обычно преследует не абстрактную цель - узнать что что-то поменялось (хотя, иногда и такой информации бывает достаточно), а получить конкретный ответ на вопрос - что изменилось и как (сменилось имя, размер, права доступа и т.д.? Прежде, чем мы перейдем к работе с такой специфической информацией об изменениях, мы немного доработаем наш предыдущий пример и посмотрим как с помощью уже известных нам трех функций можно настроить мониторинг так, чтобы получать максимально конкретизированную (на сколько это возможно) информацию по изменениям. Итак, сначала вспомним как выглядел Execute нашего потока:
WaitForMultipleObjects
lpHandles: PWOHandleArray - указатель на массив дескрипторов. Массив не должен содержать повторяющихся THandle . bWaitAll: boolean - если значение этого параметра равно True , то функция вернет значение только в том случае, если будет получен сигнал от всех событий хэндлы которых определены в массиве. В случае, если значение флага равно False , то функция будет возвращать значения при срабатывание любого из событий, при этом возвращаемое значение будет указывать на объект, который изменил состояние. dwMilliseconds:DWORD - время в мс ожидания события. Если значение этого параметра равно INFINITE , то функция будет возвращать значение только при срабатывании одного из событий. Как видите, работа с несколькими дескрипторами событий оказалось не на много сложнее, чем с одним, но используя метод WaitForMultipleObjects мы смогли боле точно идентифицировать изменения в директории. И теперь мы вплотную подходим к ситуации, когда помимо получения сигнала об изменениях в определенной директории от нас требуется определить и то, какой именно объект был изменен. Использование функций CreateFile и ReadDirectoryChangesWПрежде, чем перейдем к примеру на Delphi, посмотрим на описание этих двух функций.Функция CreateFile создает или открывает файл или другое устройство ввода/вывода (файл, файловый поток, каталог и т.д. CreateFile
dwDesiredAccess: cardinal - флаг, определяющий режим доступа к файлу или устройству (чтение, запись, чтение и запись). Наиболее часто используемые значения GENERIC_READ, GENERIC_WRITE, или сразу оба (GENERIC_READ or GENERIC_WRITE). dwShareMode:cardinal - один или несколько флагов, определяющих режим совместного доступа к файлу или устройству. Для установки значений могут использоваться следующие константы: FILE_SHARE_DELETE (0x00000004) - разрешает другим процессам получать доступ к файлу или устройству в том числе и выполнять операции удаления. FILE_SHARE_READ (0x00000001) - разрешает другим процессам выполнять операции чтения. FILE_SHARE_WRITE (0x00000002) - разрешает другим процессам выполнять операции записи. lpSecurityAttributes:PSecurityAttributes - указатель на структуру SECURITY_ATTRIBUTES , содержащую дополнительные параметры безопасности работы с файлом или устройством. Этому параметру можно присваивать значение nil. dwCreationDisposition:cardinal - флаг, который определяет какие действия следует применить к файлу или устройству. Для всех устройств, кроме файлов, этот параметр обычно устанавливается в значение OPEN_EXISTING . Также может принимать следующие значения: CREATE_ALWAYS (2) - всегда создавать новый файл. Если файл уже существует и доступен для записи, то код последней ошибки устанавливается в значение ERROR_ALREADY_EXISTS (183) . Если файл не существует и указан правильный к нему путь, то создается новый файл. а код последней ошибки устанавливается в 0. CREATE_NEW (1) - создать новый файл. Если файл с таким именем уже существует, то вернется код ошибки ERROR_FILE_EXISTS (80) . OPEN_ALWAYS (4) - всегда открывать файл. Если файл существует, то функция завершится успешно, а код последней ошибки будет установлен в значение ERROR_ALREADY_EXISTS (183) . Если же файл не существует и указан верный путь, то создастся новый файл, а значение код последней ошибки будет установлено в 0. OPEN_EXISTING (3) - открыть файл или устройство, только если файл или устройство существуют. Если объект не найден, то вернется код ошибки ERROR_FILE_NOT_FOUND (2) . TRUNCATE_EXISTING (5) - открыть файл и урезать его размер до 0 байт. Если файл не существует, то вернется код ошибки ERROR_FILE_NOT_FOUND (2). dwFlagsAndAttributes:cardinal - сочетание флагов и атрибутов файла или устройства. Значения обычно начинаются с FILE_ATTRIBUTE_* или FILE_FLAG_*. Наиболее часто используется значение FILE_ATTRIBUTE_NORMAL . При использовании функции CreateFile совместно с ReadDirectoryChangesW необходимо использовать также флаг FILE_FLAG_BACKUP_SEMANTICS . hTemplateFile: THandle - дескриптор временного файла с режимом доступа GENERIC_READ . Значение этого параметра может быть равно nil. ReadDirectoryChangesW
lpBuffer:pointer - указатель на буфер в который буду записываться обнаруженные изменения. Структура записей в буфере соответствует структуре FILE_NOTIFY_INFORMATION (см. описание ниже). Буфер может записываться как синхронно, так и асинхронно в зависимости от заданных параметров. nBufferLength:cardinal - размер буфера lpBuffer в байтах. bWatchSubtree:boolean - True указывает на то, что в результаты мониторинга будут попадать также изменения в подкаталогах. dwNotifyFilter:cardinal - фильтр событий. Может состоять из одного или нескольких флагов: FILE_NOTIFY_CHANGE_FILE_NAME (0x00000001) - любое изменение имени файла. Сюда же относятся и операции удаления или добавления файла в каталог или подкаталог. FILE_NOTIFY_CHANGE_DIR_NAME (0x00000002) - любое изменение имени подкаталога, включая добавление и удаление. FILE_NOTIFY_CHANGE_ATTRIBUTES (0x00000004) - изменение атрибутов файла или каталога. FILE_NOTIFY_CHANGE_SIZE (0x00000008) - изменение размера файла. Событие будет вызвано только после того как размер файла изменится и файл будет записан. FILE_NOTIFY_CHANGE_LAST_WRITE (0x00000010) - изменение времени последней записи в файл или каталог. FILE_NOTIFY_CHANGE_LAST_ACCESS (0x00000020) - изменение времени последнего доступа к файлу или каталогу. FILE_NOTIFY_CHANGE_CREATION (0x00000040) - изменение времени создания файла или каталога. FILE_NOTIFY_CHANGE_SECURITY (0x00000100) - изменение параметров безопасности файла или каталога. lpBytesReturned:cardinal - в случае синхронного вызова функции этот параметр будет содержать количество байт информации, записанной в буфер. Для асинхронных вызовов значение этого параметра остается неопределенным. lpOverlapped:POVERLAPPED - указатель на структуру OVERLAPPED , которая поставляет данные, которые будут использоваться во время асинхронной операции. Это значение может быть равным nil . lpCompletionRoutine:POVERLAPPED_COMPLETION_ROUTINE - указатель на callback-функцию. Этот параметр может устанавливаться в значение nil . Описания функций есть. Теперь напишем с помощью этих двух функций новую заготовку для Execute нашего потока для мониторинга изменений в директориях и файлах Windows:
FILE_NOTIFY_INFORMATION Так как в Delphi описания этой структуры я не обнаружил, но приведу е описание прямо из MSDN:
Action - сдержит информацию по произведенным изменениям и может принимать одно из следующих значений: FILE_ACTION_ADDED (0x00000001) - новый файл был добавлен в директорию или поддиректорию FILE_ACTION_REMOVED (0x00000002) - файл или директория были удалены FILE_ACTION_MODIFIED (0x00000003) - файл был изменен FILE_ACTION_RENAMED_OLD_NAME (0x00000004) - файл или директория были переименованы и FileName содержит старое имя файла или директории FILE_ACTION_RENAMED_NEW_NAME (0x00000005) - файл или директория были переименованы и FileName содержит новое имя файла или директории FileNameLength - длина имени файла. FileName[1] - указатель на строку, содержащую имя файла. То, что структура FILE_NOTIFY_INFORMATION не определена ещё не значит, что мы её не способны определить сами. Давайте определим в модуле Monitor.pas следующий тип данных:
На этом рассмотрение темы "Мониторинг изменений в директориях и файлах средствами Delphi" можно было бы считать законченным, но есть несколько моментов, о которых следует сказать. В целом можно отметить, что использование этого способа контроля изменений в директориях вместо использования всякого рада таймеров, речь о которых шла в первой части, является более предпочтительным и правильным выбором. Однако иногда можно столкнуться с некоторыми моментами, когда решения проблемы не "лежит на поверхности". К примеру, может возникнуть проблема отслеживания изменений в файлах Microsoft Office. Дело в том, что Word, Excel и PowerPoint, несмотря на то, что относятся к одному пакету программ, сохраняют свои файлы разными способами. Тот же Word при открытии файла создает временный скрытый файл, куда заносятся изменения, а после нажатия кнопки "Сохранить", вначале копирует всё содержимое файла в новый временный файл и только потом переименовывает его. У Excel алгоритм сохранения и работы с файлом ещё более "замороченный". И все эти создания временных файлов, пересохранения и переименования временных файлов каждый раз будут "провоцировать" наш поток выдавать новые и новые записи по изменениям. И тут, по крайней мере пока, я ещё не нашел более-менее универсального способа "отсеивания" лишних событий на этапе их получения, кроме как воспользоаться способом представленным в самом первом примере этого поста, т.е. разбивать фильтр на несколько событий и каждое событие анализировать по отдельности. Так что, если у Вас есть более правильное решение вопроса "Как правильно отлавливать изменение в файле MS Office?", то буду благодарен, если предложите его решение в комментариях к этому посту. Советы по программированию на DELPHI (ч.4). Как посадить приложение «на диету»!. Способы сохранения и загрузки параметров программного обеспечения (документация). Синхронизация процессов при работе с Windows (документация). Уменье заработать на ошибках. Главная » Delphi |
© 2024 Team.Furia.Ru.
Частичное копирование материалов разрешено. |