VC++ 專案需遍歷目錄樹時,MFC `CFileFind` 提供便利封裝。本文涵蓋專案屬性設定、標頭檔引用、遞迴掃描完整程式碼、關鍵 API (`FindFile`/`FindNextFile`/`IsDirectory`/`IsDots`)、多位元組/Unicode 相容宏 (`_T`, `LPCTSTR`)、`TRACE` 除錯輸出與常見錯誤。
#include
#include
void Recurse(LPCTSTR path) {
CFileFind finder;
CString wildcard(path);
wildcard += _T("\\*.*"); // _T 宏適配 MBCS/Unicode
BOOL working = finder.FindFile(wildcard);
while (working) {
working = finder.FindNextFile(); // 必須先呼叫 FindNextFile 再存取其他屬性
if (finder.IsDots()) continue; // 跳過 "." 與 ".."
if (finder.IsDirectory()) {
CString fullPath = finder.GetFilePath();
TRACE(_T("%s\n"), (LPCTSTR)fullPath); // Debug 輸出
std::wcout << fullPath.GetString() << std::endl; // 主控台輸出 (Unicode)
Recurse(fullPath); // 遞迴
}
// 檔案處理可在這裡加入
// else { ... finder.GetFileName() ... }
}
finder.Close();
}
int main() {
Recurse(_T("C:\\")); // 從 C 槽根目錄開始 (需權限)
return 0;
}
| 方法 | 用途 | 注意事項 |
|---|---|---|
| `FindFile(wildcard)` | 啟動搜尋,回傳 BOOL | 支援萬用字元 `*`, `?` |
| `FindNextFile()` | 移至下一項,**必須在存取屬性前呼叫至少一次** | 未呼叫直接 `GetFileName()` 會斷言失敗 |
| `IsDots()` | 檢查是否為 "." 或 ".." | 避免無限遞迴 |
| `IsDirectory()` | 判斷目錄 | 含重新解析點/連結 |
| `GetFilePath()` | 取得完整路徑 | 回傳 `CString` |
| `GetFileName()` | 取得檔名 | 不含路徑 |
| `GetLength()` | 檔案大小 (位元組) | `ULONGLONG` (64-bit) |
| `GetCreationTime()`/`GetLastWriteTime()` | 時間戳 | 輸出 `CTime` 物件 |
| `Close()` | 釋放搜尋控制代碼 | RAII 風格建議自行封裝 |
#include
#include
namespace fs = std::filesystem;
void scan(const fs::path& root) {
for (auto& entry : fs::recursive_directory_iterator(root)) {
if (entry.is_directory()) {
std::cout << "[DIR] " << entry.path() << '\n';
} else {
std::cout << "[FILE] " << entry.path() << " (" << entry.file_size() << " bytes)\n";
}
}
}
優勢:標準庫、跨平台、無 MFC 相依、支援迭代器範圍 for、錯誤處理用 `std::error_code` 避免例外。
| 現象 | 原因 | 解決 |
|---|---|---|
| 編譯錯誤 `CFileFind` 未定義 | 未設定「使用 MFC」或未包含 `afx.h` | 專案屬性設定 MFC、包含 `afx.h` |
| 遞迴無限循環 | 未 `continue` 跳過 `IsDots()` | 務必 `if (finder.IsDots()) continue;` |
| `GetFilePath()` 回傳空 | 未先呼叫 `FindNextFile()` | 循環開頭 `working = finder.FindNextFile();` |
| 權限拒絕 (Access Denied) | 掃描系統目錄 (System Volume Information 等) | 捕捉 `CFileException` 或檢查 `GetLastError()`,略過不可讀目錄 |
| 路徑超過 MAX_PATH (260) | 深層目錄 | 使用 `\\?\` 前綴或改用 ` |