Коли ізмененяются папки, C / C + +, Програмування, статті

Переклад А.І. Легалова, SoftCraft

Англомовний оригінал знаходиться на сервері компанії Reliable Software

Ви коли-небудь задалися питанням: яким Оразов Провідник (Explorer) дізнається про те, що деякий дія повинна модифікувати його вікно, тому що був доданий або видалений файл в поточній папці деяким зовнішнім додатком? Більше цього можна не дивуватися, тому що використання нашого Активного Об’єкту дозволяє робити те ж саме і навіть більше. Є кілька простих викликів API, за допомогою яких Ви можете запитати у файлової системи, щоб вона вибірково повідомила Вам щодо змін для файлів і папок. Як тільки Ви встановлюєте таку вахту, ваш потік може відправлятися спати, очікуючи приходу подій. Файлова система відреагує на подію, як тільки вона виявить вид зміни, за якими ви спостерігаєте.

Завантаження вихідних текстів програми FolderWatcher (Zip архів 11K).

Без подальшої суєти успадкуємо FolderWatcher з ActiveObject. Задамо в якості джерела повідомлення – подія, а в каяестве приймача повідомлення – дескриптор до вікна, що відповідає на повідомлення. Початкове подія встановлено в конструкторі FolderWatcher. Важливо також запустити утримуваний потік в кінці конструктора.

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. Дескриптор цієї події повернутий із дзвінка. Ми запам’ятовуємо цей дескриптор і використовуємо його пізніше, щоб або здійснити Восстанавленіе або відмовитися від нашого інтересу до подальших повідомленнями. Зверніть увагу, що ми можемо встановлювати наблююденіе рекурсивно, тобто, спостерігати дану папку і всі її підпапки і під-підпапки. Ми можемо також висловлювати інтерес до специфічних змін, передаючи порозрядне АБО для будь-якої комбінації наступних прапорців:

Для зручності ми визначили декілька підкласів від 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:
          FolderChangeEvent (char const * folder)
              : FileChangeEvent (folder, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME)
          {}
      };
      class TreeChangeEvent : public FileChangeEvent
      {
      public:
          TreeChangeEvent (char const * root)
              : FileChangeEvent (root, TRUE,
                                 FILE_NOTIFY_CHANGE_FILE_NAME
                               | FILE_NOTIFY_CHANGE_DIR_NAME)
          {}
      };

Повинно бути тепер просто узагальнити цей приклад, щоб зробити деяку дійсно корисну роботу в ваших програмах. Не забудьте подивитися API, який ми використовуємо в цих навчальних програмах, в інтерактивної довідці, яка йде з вашим компілятором.

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*