使用 Shell 函式 SHBrowseForFolder 顯示資料夾選擇對話框,需處理 COM 初始化、BROWSEINFO 設定、PIDL 回傳值轉換完整路徑。本文完整實作與關鍵結構解析。
CoInitialize(NULL) 初始化 COM 單元BROWSEINFO 結構SHBrowseForFolder 取得 PIDLIST_ABSOLUTESHGetPathFromIDList 將 PIDL 轉為檔案系統路徑CoTaskMemFree 釋放 PIDL 記憶體CoUninitialize 清理 COM
BROWSEINFO bi = {0};
bi.hwndOwner = hWnd; // 擁有者視窗
bi.pidlRoot = NULL; // 從桌面開始
bi.pszDisplayName = displayName; // 接收顯示名稱緩衝區 (MAX_PATH)
bi.lpszTitle = L"請選擇資料夾"; // 對話框標題
bi.ulFlags = BIF_RETURNONLYFSDIRS // 僅回傳檔案系統目錄
| BIF_DONTGOBELOWDOMAIN // 不顯示網路鄰域以下
| BIF_NEWDIALOGSTYLE; // Vista+ 新樣式(可調整大小)
bi.lpfn = NULL; // 無回呼函式
bi.lParam = 0;
| 旗標 | 效果 |
|---|---|
BIF_RETURNONLYFSDIRS | 僅允許選擇檔案系統目錄(排除虛擬資料夾) |
BIF_DONTGOBELOWDOMAIN | 網路層級停在域/工作群組,不深入伺服器 |
BIF_NEWDIALOGSTYLE | 可調整大小、支援新 UI、預設展開 |
BIF_EDITBOX | 包含編輯框可手動輸入路徑 (舊版) |
BIF_VALIDATE | 配合回呼驗證輸入路徑有效性 |
BIF_BROWSEINCLUDEFILES | 允許選擇檔案(非僅資料夾) |
BIF_SHAREABLE | 包含共享資源 |
case IDM_BROWSE_FOLDER: {
BROWSEINFO bi = {0};
wchar_t displayName[MAX_PATH] = {0};
wchar_t fullPath[MAX_PATH] = {0};
bi.hwndOwner = hWnd;
bi.pidlRoot = NULL;
bi.pszDisplayName = displayName;
bi.lpszTitle = L"請選擇資料夾";
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_DONTGOBELOWDOMAIN | BIF_NEWDIALOGSTYLE;
CoInitialize(NULL);
PIDLIST_ABSOLUTE pidl = SHBrowseForFolder(&bi);
if (pidl) {
// PIDL 轉完整路徑
if (SHGetPathFromIDList(pidl, fullPath)) {
HWND hDisplay = GetDlgItem(hDlg, IDC_DISPLAY);
SetWindowText(hDisplay, fullPath);
}
// 釋放 PIDL (COM 記憶體分配器)
CoTaskMemFree(pidl);
}
CoUninitialize();
break;
}
IMalloc (Shell 分配器) 管理,**必須用 CoTaskMemFree 釋放**SHGetPathFromIDList 僅對檔案系統物件有效,虛擬資料夾返回 FALSE


FOS_PICKFOLDERS 模式,COM 介面、支援自訂、非同步。
IFileDialog* pfd;
CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pfd));
pfd->SetOptions(FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM);
pfd->Show(hWnd);
IShellItem* psi; pfd->GetResult(&psi);
PWSTR path; psi->GetDisplayName(SIGDN_FILESYSPATH, &path);
// 使用 path... CoTaskMemFree(path);
FolderBrowserDialog / OpenFileDialog (設定 ValidateNames=false + CheckFileExists=false + 自訂)。CoInitialize/CoUninitialize 配對 → COM 錯誤、記憶體洩漏。free() 或 delete 釋放 PIDL → 堆積損毀,必須 CoTaskMemFree。pszDisplayName 緩衝區過小 → 截斷,建議 MAX_PATH (260) 或更大。SHGetPathFromIDList 返回值直接使用路徑 → 虛擬資料夾導致空字串。