11.15 監控目錄文件變化

監視對指定目錄的更改,並將有關更改的信息打印到控制檯,該功能的實現不僅可以在內核層,在應用層同樣可以。程序中使用ReadDirectoryChangesW函數來監視目錄中的更改,並使用FILE_NOTIFY_INFORMATION結構來獲取有關更改的信息。

ReadDirectoryChangesW 是Windows操作系統提供的一個函數,用於監視目錄的變化。它屬於Windows API的一部分,主要用於監視文件系統中目錄的修改、新增、刪除等變化,並通過回調函數嚮應用程序提供通知。

以下是該函數的聲明:

BOOL ReadDirectoryChangesW(
  HANDLE                hDirectory,
  LPVOID                lpBuffer,
  DWORD                 nBufferLength,
  BOOL                  bWatchSubtree,
  DWORD                 dwNotifyFilter,
  LPDWORD               lpBytesReturned,
  LPOVERLAPPED          lpOverlapped,
  LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);

其中:

  • hDirectory:要監視的目錄的句柄。
  • lpBuffer:接收變更通知的緩衝區。
  • nBufferLength:緩衝區的大小。
  • bWatchSubtree:如果爲 TRUE,則監視目錄樹中的所有目錄。如果爲 FALSE,則僅監視指定的目錄。
  • dwNotifyFilter:指定要監視的變更類型,可以是文件夾或文件的新增、刪除、修改等。
  • lpBytesReturned:返回實際讀取到的字節數。
  • lpOverlapped:用於異步操作的 OVERLAPPED 結構。
  • lpCompletionRoutine:指定一個回調函數,在異步操作完成時調用。

在使用這個函數時,通常會在回調函數中處理具體的文件變更信息。ReadDirectoryChangesW通常用於異步操作,因此在調用時需要提供一個OVERLAPPED結構或使用同步的方式等待變更。

如下代碼中使用CreateThread函數創建一個線程,並將MonitorFileThreadProc運行起來,此函數使用帶有FILE_LIST_directory標誌的CreateFile打開指定的目錄,該標誌允許該函數監視目錄。並使用ReadDirectoryChangesW函數讀取目錄中的更改,傳遞一個緩衝區來存儲更改,並指定要監視的更改類型。

使用WideCharToMultiByte函數將寬字符文件名轉換爲多字節文件名,並將文件名與目錄路徑連接以獲得文件的完整路徑。然後,該功能將有關更改的信息打印到控制檯。

#include <stdio.h>
#include <Windows.h>
#include <tlhelp32.h>

DWORD WINAPI MonitorFileThreadProc(LPVOID lParam)
{
  char *pszDirectory = (char *)lParam;
  BOOL bRet = FALSE;
  BYTE Buffer[1024] = { 0 };

  FILE_NOTIFY_INFORMATION *pBuffer = (FILE_NOTIFY_INFORMATION *)Buffer;
  DWORD dwByteReturn = 0;
  HANDLE hFile = CreateFile(pszDirectory, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
    NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
  if (INVALID_HANDLE_VALUE == hFile)
    return 1;

  while (TRUE)
  {
    ZeroMemory(Buffer, sizeof(Buffer));
    // 設置監控目錄回調函數
    bRet = ReadDirectoryChangesW(hFile,&Buffer,sizeof(Buffer),TRUE,
      FILE_NOTIFY_CHANGE_FILE_NAME |      // 修改文件名
      FILE_NOTIFY_CHANGE_ATTRIBUTES |     // 修改文件屬性
      FILE_NOTIFY_CHANGE_LAST_WRITE,      // 最後一次寫入
      &dwByteReturn, NULL, NULL);
    if (TRUE == bRet)
    {
      char szFileName[MAX_PATH] = { 0 };

      // 將寬字符轉換成窄字符,寬字節字符串轉多字節字符串
      WideCharToMultiByte(CP_ACP,0,pBuffer->FileName,(pBuffer->FileNameLength / 2),
        szFileName,MAX_PATH,NULL,NULL);

      // 將路徑與文件連接成完整文件路徑
      char FullFilePath[1024] = { 0 };
      strncpy(FullFilePath, pszDirectory, strlen(pszDirectory));
      strcat(FullFilePath, szFileName);

      switch (pBuffer->Action)
      {
        case FILE_ACTION_ADDED:
        {
          printf("添加: %s \n", FullFilePath); break;
        }
        case FILE_ACTION_REMOVED:
        {
          printf("刪除: %s \n", FullFilePath); break;
        }
        case FILE_ACTION_MODIFIED:
        {
          printf("修改: %s \n", FullFilePath); break;
        }
        case FILE_ACTION_RENAMED_OLD_NAME:
        {
          printf("重命名: %s", szFileName);
          if (0 != pBuffer->NextEntryOffset)
          {
            FILE_NOTIFY_INFORMATION *tmpBuffer = (FILE_NOTIFY_INFORMATION *)
              ((DWORD)pBuffer + pBuffer->NextEntryOffset);
            switch (tmpBuffer->Action)
              {
                case FILE_ACTION_RENAMED_NEW_NAME:
                {
                  ZeroMemory(szFileName, MAX_PATH);
                  WideCharToMultiByte(CP_ACP,0,tmpBuffer->FileName,
                    (tmpBuffer->FileNameLength / 2),
                    szFileName,MAX_PATH,NULL,NULL);
                  printf(" -> %s \n", szFileName);
                  break;
                }
              }
          }
          break;
        }
        case FILE_ACTION_RENAMED_NEW_NAME:
        {
          printf("重命名(new): %s \n", FullFilePath); break;
        }
      }
    }
  }
  CloseHandle(hFile);
  return 0;
}

int main(int argc, char * argv[])
{
  char *pszDirectory = "C:\\";

  HANDLE hThread = CreateThread(NULL, 0, MonitorFileThreadProc, pszDirectory, 0, NULL);
  WaitForSingleObject(hThread, INFINITE);
  CloseHandle(hThread);
  return 0;
}

運行後監控C盤所有文件的變化,並輸出如下信息;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章