Win32API ディレクトリの変更を監視する ReadDirectoryChangesW

ディレクトリの変更を監視するの変更を監視するには、ReadDirectoryChangesW関数を用いる。.NET Frameworkでは、System.IO.FileSystemWatcherがこの関数の機能に相当する。


  • ReadDirectoryChangesWのプロトタイプ
  • BOOL ReadDirectoryChangesW(
      HANDLE hDirectory,    // 監視するディレクトリのハンドル
      LPVOID lpBuffer,      // 読み取った結果を受け取る
                            //  バッファへのポインタ
      DWORD nBufferLength,  // lpBuffer の長さ
      BOOL bWatchSubtree,   // ディレクトリまたはディレクトリツリーを
                            // 監視するためのフラグ
      DWORD dwNotifyFilter, // 監視に使うフィルタ条件
      LPDWORD lpBytesReturned,  // 返されたバイト数
      LPOVERLAPPED lpOverlapped,
     // 重複 I/O 操作に必要な
                            //  構造体へのポインタ
      LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
     // 完了ルーチンへのポインタ
    );


  • lpBufferについて

  • lpBufferには、FILE_NOTIFY_INFORMATION構造体オブジェクトが格納される。


  • FILE_NOTIFY_INFORMATION構造体 は以下のように定義されている
  • typedef struct _FILE_NOTIFY_INFORMATION {
      DWORD NextEntryOffset; //次のレコードを取得するためにスキップすべきバイト数。0の場合は最後のレコードを意味する。
      DWORD Action;//変更のタイプ
      DWORD FileNameLength;//ファイル名の長さ
      WCHAR FileName[1];//ファイル名
    } FILE_NOTIFY_INFORMATION, *PFILE_NOTIFY_INFORMATION;


  • 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_INFORMATION構造体のActionメンバについて

  • FILE_ACTION_ADDEDディレクトリにファイルが追加された
    FILE_ACTION_REMOVEDディレクトリからファイルが削除された
    FILE_ACTION_MODIFIEDファイルが修正された(ファイルのタイムスタンプまたは属性が変更されたことも含む)。
    FILE_ACTION_RENAMED_OLD_NAMEファイル名が変更され、これは古いファイル名である。
    FILE_ACTION_RENAMED_NEW_NAMEファイル名が変更され、これは新しいファイル名である。



  • 使用例の前に注意点

  • 1.ディレクトリのハンドルは、CreateFileでオープンするが、アクセス権の指定にFILE_LIST_DIRECTORYを指定する必要がある。また、ファイルの作成方法にFILE_FLAG_BACKUP_SEMANTICSを指定する必要がある。

    HANDLE hDir = CreateFile("C:\\", 
                    FILE_LIST_DIRECTORY, 
                    0, 
                    NULL, 
                    OPEN_EXISTING, 
                    FILE_FLAG_BACKUP_SEMANTICS, 
                    NULL);



    2.FILE_NOTIFY_INFORMATION構造体のNextEntryOffsetメンバは、自分の開発環境(Windows Vista, Visual Studio 2005)では実際の2倍の数値が格納されている様であったので、NextEntryOffsetの値を2で割った値を用いるようにした。



  • 使用例
  • #include <windows.h>
    #include <stdio.h>
    #include <locale.h>
    
    int main()
    {
      //ワイド文字を文字化けさせずに出力するためにロケールを設定する
      setlocale(LC_ALL, setlocale(LC_CTYPE, ""));
    
      const wchar_t *BaseDirName = L"C:\\";
      HANDLE hDir = CreateFileW(BaseDirName, 
                            FILE_LIST_DIRECTORY, 
                            FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 
                            NULL, 
                            OPEN_EXISTING, 
                            FILE_FLAG_BACKUP_SEMANTICS, 
                            NULL);
    
      if (hDir == INVALID_HANDLE_VALUE) {
        wprintf(L"CreateFile failed.");
        return 1;
      }
    	
      while (1) {
        wchar_t lpBuffer[1024] = {L'\0'};
        DWORD dwBytesReturned;
        DWORD dwNotifyFilter;
        BOOL bRet;
    
        //監視に使うフィルタ条件
        dwNotifyFilter = 
          FILE_NOTIFY_CHANGE_FILE_NAME   |  //ファイル名変更
          FILE_NOTIFY_CHANGE_DIR_NAME    |  //ディレクトリ名変更
          FILE_NOTIFY_CHANGE_ATTRIBUTES  |  //属性変更
          FILE_NOTIFY_CHANGE_LAST_WRITE  |  //最終書き込み日時変更
          FILE_NOTIFY_CHANGE_LAST_ACCESS |  //最終アクセス日時変更
          FILE_NOTIFY_CHANGE_CREATION;       //作成日時変更
    
        bRet = ReadDirectoryChangesW(hDir,  //ディレクトリのハンドル
                                  lpBuffer, //FILE_NOTIFY_INFORMATION構造体へのポインタ
                                  sizeof(lpBuffer)/sizeof(lpBuffer[0]), //lpBufferのサイズ
                                  TRUE, //サブディレクトリを監視するためのフラグ
                                  dwNotifyFilter, //監視に使うフィルタ条件
                                  &dwBytesReturned, //返されたバイト数
                                  NULL, //重複I/O操作に必要な構造体へのポインタ
                                  NULL);//完了ルーチンへのポインタ
    
        if (!bRet) {
          wprintf(L"ReadDirectoryChangesW failed.");
          break;
        }
    
        int i = 0;
        while (1) {
          FILE_NOTIFY_INFORMATION *lpInfomation
            = (FILE_NOTIFY_INFORMATION *)&lpBuffer[i];
    
          wchar_t filename[1024] = {L'\0'};
          const size_t length = sizeof(filename)/sizeof(filename[0]);
          lpInfomation->FileName[lpInfomation->FileNameLength/sizeof(wchar_t)] = L'\0';
          _snwprintf_s(filename, 
            length,
            length,
            L"%s%s", 
            BaseDirName, lpInfomation->FileName);
          
          switch (lpInfomation->Action) {
            case FILE_ACTION_ADDED:
              wprintf(L"FILE_ACTION_ADDED: %s\n", filename);
              break;
            
            case FILE_ACTION_REMOVED:
              wprintf(L"FILE_ACTION_REMOVED: %s\n", filename);
              break;
    
            case FILE_ACTION_MODIFIED:
              wprintf(L"FILE_ACTION_MODIFIED: %s\n", filename);
              break;
                    
            case FILE_ACTION_RENAMED_OLD_NAME:
              wprintf(L"FILE_ACTION_RENAMED_OLD_NAME: %s\n", filename);
              break;
                    
            case FILE_ACTION_RENAMED_NEW_NAME:
              wprintf(L"FILE_ACTION_RENAMED_NEW_NAME: %s\n", filename);
              break;
            
            default:
              wprintf(L"Unknown File Action: %s\n", filename);
              break;
          }
    			
          if (lpInfomation->NextEntryOffset == 0) {
            break;
          }
          i += lpInfomation->NextEntryOffset / 2;
        }
      }
    
      CloseHandle(hDir);
      
      return 0;
    }


  • 実際の実行結果の一部
  • FILE_ACTION_MODIFIED: C:\WINDOWS\System32\config\SOFTWARE.LOG1
    FILE_ACTION_MODIFIED: C:\WINDOWS\System32\config\SOFTWARE
    FILE_ACTION_MODIFIED: C:\WINDOWS\System32\config\SOFTWARE
    FILE_ACTION_ADDED: C:\Program Files\PostgreSQL\8.4\data\pg_stat_tmp\pgstat.tmp
    FILE_ACTION_MODIFIED: C:\Program Files\PostgreSQL\8.4\data\pg_stat_tmp
    FILE_ACTION_MODIFIED: C:\Program Files\PostgreSQL\8.4\data\pg_stat_tmp\pgstat.tmp
    FILE_ACTION_REMOVED: C:\Program Files\PostgreSQL\8.4\data\pg_stat_tmp\pgstat.tmp
    
    ....................................................


    参考
    http://msdn.microsoft.com/ja-jp/library/cc429676.aspx
    http://support.microsoft.com/kb/245214/ja