利用 Windows Shell API(shell32.dll、shlobj.h)可輕鬆實作類似「網路芳鄰」的目錄樹瀏覽功能。本文提供完整 C++Builder 程式碼,展示如何透過 IShellFolder、PIDL、IMalloc 等核心介面遞迴列舉網路資源並填充 TTreeView。
核心類別與介面速查
| 介面/結構 | 用途 |
IShellFolder | Shell 命名空間資料夾介面,提供列舉、繫結、取得顯示名稱等方法 |
LPITEMIDLIST (PIDL) | 項目識別清單,Shell 命名空間中物件的唯一識別碼(類似檔案系統路徑) |
IMalloc | Shell 記憶體配置器,用於 PIDL、STRRET 等結構的配置/釋放 |
SHFILEINFO | 取得檔案/資料夾圖示、顯示名稱、屬性等資訊 |
STRRET | 字串回傳結構,支援 ANSI、Unicode、Offset 三種格式 |
關鍵實作步驟
- 初始化 Shell 環境:
SHGetMalloc 取得 IMalloc、SHGetDesktopFolder 取得桌面 IShellFolder、SHGetSpecialFolderLocation(CSIDL_NETWORK) 取得網路芳鄰 PIDL。
- 建立根節點:透過
GetDisplayNameOf 取得顯示名稱,SHGetFileInfo 取得系統圖示索引,加入 TTreeView。
- 遞迴列舉子資料夾(
BrowseFolder):
BindToObject 取得子資料夾的 IShellFolder
EnumObjects(SHCONTF_FOLDERS | SHCONTF_INCLUDEHIDDEN) 取得列舉器
- 迴圈
Next 取得每個子項 PIDL
MergeIDList 合併父 PIDL 與子 PIDL 形成完整路徑 PIDL
SHGetFileInfo 取得圖示與顯示名稱
- 若具
SFGAO_HASSUBFOLDER 屬性,加入虛擬子節點「PP」實現延遲載入
- 將
TDir 結構(含 ParentShellFolder、pidl、ppidl、Sfogliato)掛載至 TreeNode->Data
- 動態展開(
TreeViewExpanding):節點展開時檢查 Sfogliato 標記,未瀏覽過則呼叫 BrowseFolder 載入子節點並移除虛擬節點。
- 點選顯示路徑(
TreeViewClick):SHGetPathFromIDList 將 PIDL 轉為檔案系統路徑(若適用)。
關鍵輔助函式
// 合併兩個 PIDL
LPITEMIDLIST MergeIDList(LPITEMIDLIST a, LPITEMIDLIST b) {
int cb1 = a ? GetPIDLSize(a) - 2 : 0;
int cb2 = b ? GetPIDLSize(b) - 2 : 0;
LPITEMIDLIST result = (LPITEMIDLIST)m_alloc->Alloc(cb1 + cb2 + 2);
CopyMemory(result, a, cb1);
CopyMemory((LPBYTE)result + cb1, b, cb2);
FillMemory((LPBYTE)result + cb1 + cb2, 2, 0);
return result;
}
// 計算 PIDL 總大小(含終止雙零)
int GetPIDLSize(LPITEMIDLIST pidl) {
unsigned short cb = 0;
while (pidl) {
cb += pidl->mkid.cb;
pidl = GetNextItem(pidl);
}
return cb + 2;
}
// 取得下一個 PIDL 項目
LPITEMIDLIST GetNextItem(LPITEMIDLIST pidl) {
unsigned short len = pidl->mkid.cb;
return len ? (LPITEMIDLIST)((LPBYTE)pidl + len) : NULL;
}
記憶體管理鐵律
- 所有
IMalloc::Alloc 配置的記憶體(PIDL、STRRET 緩衝區)必須配對 Free 釋放。
IEnumIDList::Release、IShellFolder::Release 遵循 COM 參考計數規則。
TDir 結構體由 new 配置,需在節點刪除或表單銷毀時手動 delete(範例中省略,實務必補全)。
常見坑點與對策
| 問題 | 原因 | 解決方案 |
| 網路芳鄰顯示空白 | 權限不足或網路探索服務未啟動 | 檢查「網路探索」、「檔案與印表機共用」防火牆規則 |
| 展開節點卡死 | 遠端主機回應慢導致 UI 執行緒阻塞 | 改用背景執行緒列舉,完成後 PostMessage 通知 UI 更新 |
| 圖示顯示異常 | SHGetFileInfo 旗標組合錯誤 | 確認 SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON 組合正確 |
| Unicode 路徑亂碼 | STRRET_WSTR 未正確轉換 | 使用 WideCharToMultiByte(CP_UTF8...) 或直接支援 Unicode 版本 API |
完整程式碼已整合於表單 TForm1 中,包含建構子初始化、BrowseFolder 遞迴列舉、GetNome 多格式名稱解析、PIDL 合併/大小計算、樹狀視圖事件處理。此架構易於擴充為完整的 Shell 瀏覽器元件。
- 上一篇文章: C++ 編譯器選擇指南:Bjarne Stroustrup 親授的避坑清單
- 下一篇文章: Managed C++ 開發自訂 Web 組合控件:從零打造可複用的 SearchControl
Copyright ©
程式碼之家 All Rights Reserved