Происходит следующее: удерживаемый поток делает другой вызов 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);
if (INVALID_HANDLE_VALUE == _handle) throw WinException("Cannot create change notification handle");
}
~FileChangeEvent() {
if (INVALID_HANDLE_VALUE != _handle) FindCloseChangeNotification(_handle);
}
operator HANDLE() const { return _handle; }
BOOL ContinueNotification() {
return FindNextChangeNotification(_handle);
}
private:
HANDLE _handle;
};
class FolderChangeEvent : public FileChangeEvent {
public: