>class FolderWatcher : public ActiveObject {
>public: FolderWatcher(char const* folder, HWND hwnd) : _notifySource (folder), _hwndNotifySink (hwnd) {
> strcpy(_folder, folder);
> _thread.Resume();
> }
> ~FolderWatcher() {
> Kill();
> }
>private:
> void InitThread() {}
> void Loop();
> void FlushThread() {}
> FolderChangeEvent _notifySource;
> HWND _hwndNotifySink;
> char _folder[MAX_PATH];
>};
Все действия в ActiveObject происходят внутри метода Loop. Здесь мы устанавливаем "бесконечный" цикл, в котором поток должен ожидать событие. Когда событие происходит, мы проверяем флажок _isDying (как обычно) и посылаем специальное сообщение WM_FOLDER_CHANGE окну, которое имеет дело с уведомлениями. Это — не предопределенное сообщение Windows. Оно специально определено нами для передачи уведомления о папке от одного потока другому.
Происходит следующее: удерживаемый поток делает другой вызов API, чтобы позволить файловой системе, узнать, что она нуждается в большем количестве уведомлений. Затем управление возвращается к ожидающему потоку, находящемуся в состоянии сна. Одновременно Windows получает наше сообщение WM_FOLDER_CHANGE из очереди сообщений и посылает его оконной процедуре принимающего окна. Подробности чуть позже.
>UINT const WM_FOLDER_CHANGE = WM_USER;
>void FolderWatcher::Loop() {
> for (;;) {
> // Wait for change notification
> DWORD waitStatus = WaitForSingleObject(_notifySource, INFINITE);
> if (WAIT_OBJECT_0 == waitStatus) {
> // If folder changed
> if (_isDying) return;
> PostMessage(_hwndNotifySink, WM_FOLDER_CHANGE, 0, (LPARAM)_folder);
> // Continue change notification
> if (!_notifySource.ContinueNotification()) {
> // Error: Continuation failed
> return;
> }
> } else {
> // Error: Wait failed
> return;
> }
> }
>}
Рассмотрим, что происходит в оконной процедуре в ответ на наше специальное сообщение. Мы вызываем метод Контроллера OnFolderChange. Этот метод может делать все, что мы захотим. В Проводнике (Explorer) он регенерирует отображение содержимого папки, которую мы наблюдаем. В нашем примере он только вызывает простое окно сообщения. Обратите внимание, что мы передаем имя измененной папки как LPARAM. Совершенно неважно, как определить WPARAM и LPARAM, в сообщении, определяемом пользователем.
Между прочим, Наблюдатель Папки — только часть Контроллера.
>case WM_FOLDER_CHANGE:
> pCtrl->OnFolderChange(hwnd, (char const *)lParam);
> return 0;
>void Controller::OnFolderChange(HWND hwnd, char const * folder) {
> MessageBox(hwnd, "Change Detected, "Folder Watcher", MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_OK);
>}
>class Controller {
>public:
> Controller(HWND hwnd, CREATESTRUCT * pCreate);
> ~Controller();
> void OnFolderChange(HWND hwnd, char const *folder);
>private:
> FolderWatcher _folderWatcher;
>};
Теперь, когда мы знаем, как иметь дело с уведомлением, давайте взглянем на их источники, События изменяющие файлы. Объект события создан файловой системой в ответ на FindFirstChangeNotification. Дескриптор этого события возвращен из вызова. Мы запоминаем этот дескриптор и используем его позже, чтобы или осуществить восстанавление или отказаться от нашего интереса к дальнейшим уведомлениям. Обратите внимание, что мы можем устанавливать наблююдение рекурсивно, то есть, наблюдать данную папку и все ее подпапки и подподпапки. Мы можем также выражать интерес к специфическим изменениям, передавая поразрядное ИЛИ для любой комбинации следующих флажков:
• 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_SECURITY
Для удобства мы определили несколько подклассов от FileChangeEvent, которые соответствуют к некоторым полезным комбинациям этих флажков. Один из них — FolderChangeEvent, который мы использовали в нашем FolderWatcher.
>class FileChangeEvent {
>public:
> FileChangeEvent(char const *folder, BOOL recursive, DWORD notifyFlags) {
> _handle = FindFirstChangeNotification(folder, recursive, notifyFlags);