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

C++ Builder 目錄操作全攻略:判斷存在、選擇對話框、多級建立與遞歸刪除

     編輯:關於C++

在 C++ Builder 開發中,目錄 (資料夾) 操作是高頻需求。VCL 雖提供 ForceDirectories、RemoveDir 等封裝,但缺乏「判斷目錄存在」、「圖形化選擇目錄」、「多級目錄一次建立/刪除」的直觀方法。本文系統整理四大核心場景,提供基於 Windows API 的完整實作程式碼,並解析關鍵細節與遞歸邏輯。

一、判斷目錄是否存在

VCL 的 FileExists 僅適用檔案。目錄判斷需用 FindFirstFile 搭配 FILE_ATTRIBUTE_DIRECTORY 屬性檢查。

bool DirectoryExistsEx(const char* Dir) {
    if (!Dir || !*Dir) return false;
    // 移除結尾反斜線 (根目錄除外)
    int len = strlen(Dir);
    if (len > 3 && Dir[len-1] == '\\') len--; 
    
    WIN32_FIND_DATA wfd;
    HANDLE hFind = FindFirstFile(Dir, &wfd);
    if (hFind == INVALID_HANDLE_VALUE) return false;
    
    FindClose(hFind);
    return (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
}

關鍵點:根目錄 (如 C:\\) 不可移除反斜線,否則 FindFirstFile 會失敗。

二、圖形化選擇目錄對話框 (SHBrowseForFolder)

專業軟體常在編輯框旁放置「瀏覽」按鈕,彈出樹狀目錄選擇對話框。核心 API 為 SHBrowseForFolder,需填寫 BROWSEINFO 結構。


#include <shlobj.h>

AnsiString SelectDirectory(const AnsiString& title = "請選擇目錄") {
    char szSelected[MAX_PATH] = {0};
    char szDisplayName[MAX_PATH] = {0};
    
    BROWSEINFO bi = {0};
    bi.hwndOwner = Application->Handle;
    bi.pszDisplayName = szDisplayName;
    bi.lpszTitle = title.c_str();
    bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE; // 現代樣式、僅檔案系統目錄
    
    LPITEMIDLIST pidl = SHBrowseForFolder(&bi);
    if (pidl) {
        SHGetPathFromIDList(pidl, szSelected);
        CoTaskMemFree(pidl); // 必須釋放 PIDL 記憶體
        return AnsiString(szSelected);
    }
    return "";
}

旗標說明:BIF_NEWDIALOGSTYLE (IE5+) 提供可調整大小、支援拖放的現代對話框;BIF_EDITBOX 允許直接輸入路徑。

三、直接建立多級目錄 (遞歸實現)

Windows CreateDirectory 要求父目錄存在。遞歸函式 MakeDirectoryEx 逐層檢查並建立父目錄。


bool MakeDirectoryEx(const AnsiString& Path) {
    if (Path.IsEmpty()) return false;
    
    AnsiString dir = Path;
    // 移除結尾反斜線
    if (!dir.IsEmpty() && dir[dir.Length()] == '\\') dir = dir.SubString(1, dir.Length()-1);
    
    // 尋找父目錄
    AnsiString parent;
    for (int i = dir.Length(); i > 0; i--) {
        if (dir[i] == '\\' || dir[i] == '/') {
            parent = dir.SubString(1, i-1);
            break;
        }
    }
    if (parent.IsEmpty()) return false; // 根目錄或錯誤路徑
    
    // 遞歸建立父目錄
    if (parent.Length() > 3) { // 非磁碟根目錄
        if (!DirectoryExistsEx(parent.c_str())) {
            if (!MakeDirectoryEx(parent)) return false;
        }
    }
    
    // 建立當前目錄
    SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, FALSE };
    return CreateDirectory(dir.c_str(), &sa);
}

現代替代:VCL 實際上已提供 ForceDirectories(AnsiString) 一鍵完成此功能,本程式碼主要供理解遞歸邏輯或無 VCL 環境時參考。

四、直接刪除整個目錄樹 (遞歸刪除)

類似 DOS deltree,需遞歸枚舉子目錄/檔案並刪除,最後刪除空目錄。


bool DeleteDirectoryEx(const AnsiString& Path) {
    if (Path.IsEmpty() || Path.Length() < 4) return false; // 保護根目錄
    
    AnsiString dir = Path;
    if (dir[dir.Length()] != '\\') dir += '\\';
    AnsiString search = dir + "*.*";
    
    WIN32_FIND_DATA wfd;
    HANDLE hFind = FindFirstFile(search.c_str(), &wfd);
    bool success = true;
    
    if (hFind != INVALID_HANDLE_VALUE) {
        do {
            if (strcmp(wfd.cFileName, ".") == 0 || strcmp(wfd.cFileName, "..") == 0) continue;
            
            AnsiString fullPath = dir + wfd.cFileName;
            if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                // 遞歸刪除子目錄
                success = success && DeleteDirectoryEx(fullPath);
            } else {
                // 強制刪除檔案 (移除唯讀/隱藏/系統屬性)
                SetFileAttributes(fullPath.c_str(), FILE_ATTRIBUTE_NORMAL);
                success = success && DeleteFile(fullPath.c_str());
            }
        } while (FindNextFile(hFind, &wfd) && success);
        FindClose(hFind);
    }
    
    if (success) return RemoveDirectory(Path.c_str());
    return false;
}

最佳實踐與陷阱提醒

  • 權限問題:Vista+ 系統目錄、Program Files 需管理員權限 (UAC)。應用程式需清單提升或使用者以管理員身分執行。
  • 長路徑:MAX_PATH 限制。使用 \\?\C:\Very\Long\Path 前綴可繞過 (API 需支援)。
  • 符號連結/接合點:遞歸刪除可能進入迴圈或刪除目標資料。建議檢查 FILE_ATTRIBUTE_REPARSE_POINT 並跳過或特殊處理。
  • 檔案被佔用:DeleteFile 會失敗。可嘗試 MoveFileEx(..., MOVEFILE_DELAY_UNTIL_REBOOT) 排程重啟刪除。
  • 例外安全:C++ Builder 無 RAII 自動釋放 HANDLE,務必在所有返回路徑呼叫 FindClose。

總結

掌握 FindFirstFile/FindNextFile 枚舉模式與遞歸思想,是處理目錄樹操作的關鍵。C++ Builder 開發者日常建議優先使用 VCL 封裝 (ForceDirectories, TDirectory 等),但在需要精細控制 (權屬、過濾、進度回呼) 或無 VCL 環境時,本文提供的 Windows API 直接實作是可靠的基礎。

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