應用程式常需讓使用者選擇目錄(如匯出路徑、掃描根目錄)。C++Builder 提供兩種主要方式:Windows Shell SHBrowseForFolder(樹狀瀏覽、支援網路芳鄰)與 VCL 封裝 SelectDirectory(類似檔案對話框、可新建資料夾)。本文完整實作兩種封裝函式,並對比優缺點與適用場景。
#include // 需連結 shell32.lib
AnsiString SelectFolder(AnsiString Caption)
{
BROWSEINFO bi = {0};
LPITEMIDLIST pidl = NULL;
char displayName[MAX_PATH] = {0};
char path[MAX_PATH] = {0};
bi.hwndOwner = Application->Handle;
bi.pszDisplayName = displayName;
bi.lpszTitle = Caption.c_str();
bi.ulFlags = BIF_RETURNONLYFSDIRS | // 僅回傳檔案系統目錄
BIF_NEWDIALOGSTYLE | // Vista+ 新樣式 (可調整大小、搜尋框)
BIF_BROWSEINCLUDEFILES; // 選項:允許顯示檔案 (通常目錄選擇不需)
pidl = SHBrowseForFolder(&bi);
if (!pidl) return ""; // 使用者取消
if (SHGetPathFromIDList(pidl, path))
{
CoTaskMemFree(pidl); // 必須釋放 PIDL 記憶體
AnsiString result = path;
if (!result.IsEmpty() && result[result.Length()] != '\\')
result += '\\';
return result;
}
CoTaskMemFree(pidl);
return "";
}
| 旗標 | 效果 |
|---|---|
| BIF_RETURNONLYFSDIRS | 僅回傳檔案系統目錄(過濾虛擬資料夾如控制台、印表機) |
| BIF_DONTGOBELOWDOMAIN | 不顯示網路芳鄰下層 |
| BIF_STATUSTEXT | 在對話框顯示狀態文字 (需回呼函式) |
| BIF_RETURNFSANCESTORS | 允許選擇非檔案系統目錄的父目錄 |
| BIF_EDITBOX | 舊版:包含編輯框輸入路徑 |
| BIF_NEWDIALOGSTYLE | Vista+ 新樣式:可調整大小、搜尋、輕鬆存取 |
| BIF_BROWSEINCLUDEFILES | 樹中顯示檔案 (通常目錄選擇不勾選) |
| BIF_UAHINT | 顯示使用者存取控制提示 |
| BIF_NONEWFOLDERBUTTON | 隱藏「新建資料夾」按鈕 (新樣式預設顯示) |
int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
{
switch (uMsg)
{
case BFFM_INITIALIZED:
// 設定初始目錄
SendMessage(hwnd, BFFM_SETSELECTION, TRUE, (LPARAM)lpData);
break;
case BFFM_SELCHANGED:
// 選擇變更:啟用/禁用 OK 按鈕、更新狀態文字
// SHGetPathFromIDList((LPITEMIDLIST)lParam, buffer);
// SendMessage(hwnd, BFFM_SETSTATUSTEXT, 0, (LPARAM)buffer);
break;
case BFFM_VALIDATEFAILED:
// 驗證失敗 (輸入無效路徑)
return 1; // 1=保持對話框, 0=關閉
}
return 0;
}
// 使用回呼
bi.lpfn = BrowseCallbackProc;
bi.lParam = (LPARAM)"C:\\Initial\\Path"; // 初始目錄
#include
AnsiString SelectDir(AnsiString Dir)
{
// TSelectDirOpts 選項組合
TSelectDirOpts opts;
opts << sdAllowCreate // 允許使用者輸入新資料夾名稱
<< sdPerformCreate // 實際建立新資料夾
<< sdPrompt; // 顯示提示訊息
if (SelectDirectory(Dir, opts, 0))
return Dir;
return "";
}
| 選項 | 效果 |
|---|---|
| sdAllowCreate | 允許在編輯框輸入新資料夾名稱 |
| sdPerformCreate | 按下 OK 時實際建立資料夾 (需搭配 sdAllowCreate) |
| sdPrompt | 對話框標題列顯示提示文字 |
| sdShowEdit | 顯示編輯框 (舊樣式) |
| 特性 | SHBrowseForFolder | SelectDirectory |
|---|---|---|
| 介面風格 | 樹狀瀏覽 (類似總管左側) | 類似開啟/儲存檔案對話框 |
| 網路芳鄰支援 | 完整支援 | 不支援 |
| 新建資料夾 | 新樣式預設支援 (BIF_NONEWFOLDERBUTTON 控制) | 需 sdAllowCreate + sdPerformCreate |
| 鍵盤輸入路徑 | 新樣式支援搜尋/輸入 | 支援 (sdShowEdit) |
| 本地化 | 系統自動本地化 (中文 Windows 顯示中文) | 介面固定英文 (VCL 內建資源) |
| 自訂程度 | 高 (回呼函式可控制啟用/禁用、狀態文字、初始目錄) | 低 (僅選項旗標) |
| 相依性 | shell32.lib, shlobj.h | FileCtrl.hpp (VCL 內建) |
| 回傳值 | AnsiString 路徑 | bool 成功, 路徑由參考參數回傳 |
// 1. Shell 方式 (推薦:支援網路、本地化、新樣式)
AnsiString dest = SelectFolder("請選擇匯出資料的目標目錄");
if (!dest.IsEmpty())
{
ExportData(dest);
}
// 2. VCL 方式 (簡單、不需網路、可新建)
AnsiString dest2 = "";
if (SelectDirectory(dest2, TSelectDirOpts() << sdAllowCreate << sdPerformCreate, 0))
{
ExportData(dest2);
}
Windows Vista 引入 IFileDialog (配合 FOS_PICKFOLDERS),提供更現代的資料夾選擇體驗:
ComObj.hpp + ShlObj.hpp 使用
// 簡易範例
IFileDialog* pfd = NULL;
CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER,
IID_IFileDialog, (void**)&pfd);
DWORD options;
pfd->GetOptions(&options);
pfd->SetOptions(options | FOS_PICKFOLDERS);
pfd->Show(Application->Handle);
IShellItem* psi;
pfd->GetResult(&psi);
LPWSTR pszPath;
psi->GetDisplayName(SIGDN_FILESYSPATH, &pszPath);
AnsiString path = pszPath;
CoTaskMemFree(pszPath);
psi->Release();
pfd->Release();
SHBrowseForFolder (加 BIF_NEWDIALOGSTYLE)SelectDirectoryIFileDialog + FOS_PICKFOLDERS掌握這三種方式,即可應對從舊版相容到現代體驗的所有目錄選擇需求。