C++Builder 雖提供 FileExists 檢查檔案,但缺乏直接的目錄操作函式。本文整理四大常見目錄操作需求:判斷目錄是否存在、彈出選擇目錄對話框、建立多層目錄、遞歸刪除目錄樹,並提供完整的 Windows API 實作代碼。
利用 FindFirstFile 搭配 FILE_ATTRIBUTE_DIRECTORY 屬性判斷。
bool DirectoryExists(const char* Dir)
{
if (!Dir || !*Dir) return false;
char path[MAX_PATH];
strncpy(path, Dir, MAX_PATH - 1);
path[MAX_PATH - 1] = '\0';
// 移除結尾反斜線(根目錄 "C:\\" 除外)
size_t len = strlen(path);
if (len > 3 && path[len - 1] == '\\')
path[len - 1] = '\0';
WIN32_FIND_DATA wfd;
HANDLE hFind = FindFirstFile(path, &wfd);
if (hFind == INVALID_HANDLE_VALUE)
return false;
bool isDir = (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
FindClose(hFind);
return isDir;
}
注意:FindFirstFile 也會匹配檔案,務必檢查屬性位確認是目錄。
#include
AnsiString SelectFolder(AnsiString Caption)
{
BROWSEINFO bi = {0};
char sDir[MAX_PATH] = {0};
char FolderName[MAX_PATH] = {0};
bi.hwndOwner = Application->Handle;
bi.pszDisplayName = FolderName;
bi.lpszTitle = Caption.c_str();
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE; // Vista+ 新樣式
LPITEMIDLIST pidl = SHBrowseForFolder(&bi);
if (!pidl) return "";
if (SHGetPathFromIDList(pidl, sDir))
{
CoTaskMemFree(pidl); // 必須釋放 PIDL
AnsiString result = sDir;
if (!result.IsEmpty() && result[result.Length()] != '\\')
result += '\\';
return result;
}
CoTaskMemFree(pidl);
return "";
}
#include
AnsiString SelectDir(AnsiString Dir)
{
// sdAllowCreate: 允許使用者新建資料夾
// sdPerformCreate: 實際建立新資料夾
// sdPrompt: 提示輸入路徑
if (SelectDirectory(Dir, TSelectDirOpts() << sdAllowCreate << sdPerformCreate << sdPrompt, 0))
return Dir;
return "";
}
CreateDirectory 僅能建立單層,需遞歸或迭代分解路徑逐層建立。
bool ForceDirectories(const char* Dir)
{
if (!Dir || !*Dir) return false;
char path[MAX_PATH];
strncpy(path, Dir, MAX_PATH - 1);
path[MAX_PATH - 1] = '\0';
// 移除結尾反斜線
size_t len = strlen(path);
if (len > 0 && path[len - 1] == '\\')
path[len - 1] = '\0';
// 逐層建立
for (char* p = path + 3; *p; ++p) // 跳過 "C:\"
{
if (*p == '\\')
{
*p = '\0';
if (!DirectoryExists(path))
{
if (!CreateDirectory(path, NULL))
{
DWORD err = GetLastError();
if (err != ERROR_ALREADY_EXISTS)
{
*p = '\\';
return false;
}
}
}
*p = '\\';
}
}
// 建立最終目錄
if (!DirectoryExists(path))
return CreateDirectory(path, NULL) || GetLastError() == ERROR_ALREADY_EXISTS;
return true;
}
或直接使用 VCL 封裝 ForceDirectories(AnsiString)(SysUtils.hpp)。
bool DeleteDirectoryTree(const char* Dir)
{
if (!Dir || !*Dir) return false;
char searchPath[MAX_PATH];
snprintf(searchPath, MAX_PATH, "%s\\*.*", Dir);
WIN32_FIND_DATA wfd;
HANDLE hFind = FindFirstFile(searchPath, &wfd);
if (hFind == INVALID_HANDLE_VALUE) return false;
bool success = true;
do
{
if (strcmp(wfd.cFileName, ".") == 0 || strcmp(wfd.cFileName, "..") == 0)
continue;
char fullPath[MAX_PATH];
snprintf(fullPath, MAX_PATH, "%s\\%s", Dir, wfd.cFileName);
if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
// 遞歸刪除子目錄
if (!DeleteDirectoryTree(fullPath))
success = false;
}
else
{
// 刪除檔案(先移除唯讀屬性)
SetFileAttributes(fullPath, FILE_ATTRIBUTE_NORMAL);
if (!DeleteFile(fullPath))
success = false;
}
}
while (FindNextFile(hFind, &wfd));
FindClose(hFind);
// 最後刪除空目錄本身
SetFileAttributes(Dir, FILE_ATTRIBUTE_NORMAL);
return success && RemoveDirectory(Dir);
}
| 需求 | 推薦 API | 備註 |
|---|---|---|
| 檢查目錄存在 | FindFirstFile + FILE_ATTRIBUTE_DIRECTORY | VCL 無直接對應 |
| 選擇目錄(含網路) | SHBrowseForFolder | 介面較舊,Vista+ 加 BIF_NEWDIALOGSTYLE |
| 選擇目錄(可新建) | SelectDirectory (FileCtrl.hpp) | VCL 封裝,介面熟悉但為英文 |
| 建立多層目錄 | ForceDirectories (SysUtils.hpp) | VCL 內建,最簡便 |
| 刪除目錄樹 | 遞歸 FindFirstFile + RemoveDirectory | VCL 無內建,需自行實作 |
SetFileAttributes(..., FILE_ATTRIBUTE_NORMAL) 移除唯讀、隱藏、系統屬性,否則刪除/修改會失敗。MAX_PATH (260),超長路徑需加 \\\\?\\ 前綴並使用 Unicode 版 API(FindFirstFileW 等)。掌握這套目錄操作工具箱,即可在 C++Builder 中從容應對各類檔案系統管理需求。