首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

Windows 目錄監控:使用 ReadDirectoryChangesW 實現文件系統即時監測

     編輯:關於VC++

前言

在 Windows 環境下開發應用程式時,經常需要監控特定目錄下的文件變更(如新增、刪除、重命名、屬性修改等)。雖然 .NET Framework 提供了 FileSystemWatcher 類別,但在傳統 C++ 開發中,我們需要直接呼叫 Windows API 來實現這項功能。本文將詳細介紹如何使用 ReadDirectoryChangesW API 封裝一個高效的目錄監控類別,並提供完整的程式碼範例。

為什麼選擇 ReadDirectoryChangesW?

早期開發者常使用 FindFirstChangeNotification,但該 API 僅能通知「目錄有變更」,無法得知具體是哪個文件發生了什麼變化。經過 MSDN 文件研究,發現 ReadDirectoryChangesW 不僅支援遞迴監控整個目錄樹,還能回報具體的文件名稱與變更動作類型,是實現精細化目錄監控的最佳選擇。

核心實現:FileSystemWatcher 類別封裝

以下封裝類別採用獨立執行緒輪詢方式,支援多種變更通知過濾條件,並透過回呼函數將事件傳遞給上層應用:

// FileSystemWatcher.h
#define _WIN32_WINNT 0x0500
#include <windows.h>
#include <string>
#include <cassert>

enum ACTION { ADDED=1, REMOVED=2, MODIFIED=3, RENAMED=4 };

class FileSystemWatcher
{
public:
    typedef void (__stdcall *LPDEALFUNCTION)(ACTION act, std::string filename1, std::string filename2);

    bool Run(std::string path, LPDEALFUNCTION dealfun);
    void Close();

    FileSystemWatcher() : DealFun(NULL), hThread(NULL), hDir(INVALID_HANDLE_VALUE) {}
    ~FileSystemWatcher() { Close(); }

private:
    std::string WatchedDir;
    LPDEALFUNCTION DealFun;
    HANDLE hThread;
    HANDLE hDir;

    // 禁止複製建構與指派
    FileSystemWatcher(const FileSystemWatcher&);
    FileSystemWatcher& operator=(const FileSystemWatcher&);

    static DWORD WINAPI Routine(LPVOID lParam);
};

關鍵實現細節

1. 目錄控制代碼取得

使用 CreateFile 並指定 FILE_FLAG_BACKUP_SEMANTICS 旗標來開啟目錄:

obj->hDir = CreateFile(
    obj->WatchedDir.c_str(),
    GENERIC_READ | GENERIC_WRITE,
    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
    NULL,
    OPEN_EXISTING,
    FILE_FLAG_BACKUP_SEMANTICS,
    NULL
);

2. 變更通知緩衝區設計

配置足夠大小的緩衝區以容納多筆 FILE_NOTIFY_INFORMATION 結構:

char buf[2 * (sizeof(FILE_NOTIFY_INFORMATION) + MAX_PATH)];
FILE_NOTIFY_INFORMATION* pNotify = (FILE_NOTIFY_INFORMATION*)buf;
DWORD BytesReturned;

3. 核心監控迴路

while (true) {
    if (ReadDirectoryChangesW(
        obj->hDir,
        pNotify,
        sizeof(buf),
        TRUE,  // 遞迴監控子目錄
        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,
        &BytesReturned,
        NULL,
        NULL
    )) {
        // 處理通知資料...
        obj->DealFun((ACTION)pNotify->Action, str1, str2);
    } else {
        break; // 發生錯誤或目錄被刪除
    }
}

4. Unicode 轉 ANSI 處理

由於 API 回傳 Unicode 文件名,需轉換為多位元組字串:

WideCharToMultiByte(CP_ACP, 0, pNotify->FileName, pNotify->FileNameLength/2, tmp, 99, NULL, NULL);

測試程式範例

#include <conio.h>
#include <iostream>
using namespace std;

void __stdcall MyDeal(ACTION act, std::string filename1, std::string filename2) {
    switch (act) {
        case ADDED:    cout << "Added - " << filename1 << endl; break;
        case REMOVED:  cout << "Removed - " << filename1 << endl; break;
        case MODIFIED: cout << "Modified - " << filename1 << endl; break;
        case RENAMED:  cout << "Rename - " << filename1 << " " << filename2 << endl; break;
    }
}

void main() {
    FileSystemWatcher watcher;
    watcher.Run("D:\\", MyDeal);
    cout << "Watch D:\\" << endl;
    cout << "Press <q> to quit." << endl;
    while (getch() != 'q');
    watcher.Close();
}

注意事項與改進方向

  • 平台需求:需要 Windows NT 3.51 SP3 或更新版本,不支援 Windows 95/98
  • 錯誤處理:生產環境應加強錯誤碼檢查與日誌記錄
  • Unicode 支援:建議改用 std::wstring 完整支援 Unicode 路徑
  • 非同步 I/O:可結合 OVERLAPPED 結構實現真正的非同步通知,避免阻塞執行緒
  • 資源清理:析構函數確保正確關閉控制代碼與終止執行緒

總結

透過 ReadDirectoryChangesW 封裝的目錄監控類別,能夠滿足大多數即時文件系統監控需求。本文提供的程式碼在 Win2K + VC++6.0 環境下測試通過,實際部署時建議拆分為獨立的 .h/.cpp 檔案,並根據專案需求擴展功能(如支援網路路徑、事件過濾、記憶體池優化等)。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved