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

徹底解決 CFileDialog 擴展名隱藏導致的檔名歧義:PIDL 技術深度應用

     編輯:關於VC++

問題回顧

前作《自訂 CFileDialog 實例》留下一個尾巴:當使用者在資源管理器勾選「隱藏已知副檔名」時,列表控制項 GetItemText 僅回傳不含副檔名的檔名(如 foo),導致 foo.txt、foo.jpg、foo.doc 無法區分。

核心祕密:List Control 的 Item Data 其實是 PIDL

Windows Shell 使用 PIDL (Pointer to Item ID List) 唯一標識 Shell 物件(檔案、資料夾、連結、虛擬資料夾)。CListCtrl::GetItemData 回傳的 DWORD 正是該項目在當前資料夾中的 相對 PIDL。

解決路徑:三步取得完整物理路徑

步驟 1:取得當前資料夾的 PIDL


// 發送 CDM_GETFOLDERIDLIST 兩次:第一次取長度,第二次取資料
LPITEMIDLIST pidlFolder = NULL;
UINT cb = (UINT)SendMessage(CDM_GETFOLDERIDLIST, 0, 0);  // 取得大小
if (cb > 0) {
    pidlFolder = (LPITEMIDLIST)CoTaskMemAlloc(cb);
    SendMessage(CDM_GETFOLDERIDLIST, cb, (LPARAM)pidlFolder);
}

步驟 2:取得項目的相對 PIDL


LVITEM lvi = { 0 };
lvi.mask = LVIF_PARAM;
lvi.iItem = nIndex;
GetListCtrl().GetItem(&lvi);
LPITEMIDLIST pidlRel = (LPITEMIDLIST)lvi.lParam;  // 相對 PIDL

步驟 3:合併並解析為完整路徑


// 1. 取得資料夾的 IShellFolder 介面
IShellFolder* pFolder = NULL;
SHBindToParent(pidlFolder, IID_IShellFolder, (void**)&pFolder, NULL);

// 2. 合併 PIDL:資料夾 PIDL + 相對 PIDL = 完整 PIDL
LPITEMIDLIST pidlFull = ILCombine(pidlFolder, pidlRel);

// 3. 用 SHGDN_FORPARSING 取得完整物理路徑(含副檔名)
STRRET strret;
pFolder->GetDisplayNameOf(pidlFull, SHGDN_FORPARSING, &strret);
TCHAR szPath[MAX_PATH];
StrRetToBuf(&strret, pidlFull, szPath, MAX_PATH);
// szPath 即為 "C:\Path\foo.txt" 等完整路徑

// 4. 清理
pFolder->Release();
CoTaskMemFree(pidlFolder);
CoTaskMemFree(pidlFull);

封裝成 CFileDlgHelper::GetItemPathName


CString CFileDlgHelper::GetItemPathName(int nIndex) {
    // 內部實作上述三步驟,並處理記憶體管理、錯誤檢查
    // 呼叫端極簡單:
    // CString path = m_dlgHelper.GetItemPathName(i);
    return path;  // 保證含副檔名的完整路徑
}

實測效果

修改後程式運行畫面
圖 1:即使資源管理器隱藏副檔名,程式仍正確顯示完整路徑 "C:\Test\foo.txt"

關鍵 API 速查表

API用途
CDM_GETFOLDERIDLIST取得對話框當前資料夾 PIDL
SHBindToParent由 PIDL 取得父資料夾 IShellFolder
ILCombine合併兩個 PIDL
IShellFolder::GetDisplayNameOfPIDL 轉顯示名稱,SHGDN_FORPARSING 產生完整路徑
StrRetToBuf統一處理 STRRET 輸出緩衝區
CoTaskMemAlloc/FreeShell 記憶體管理(必須成對使用)

避坑指南

  • Unicode/ANSI:建議專案設為 Unicode,Shell API 原生支援
  • 虛擬資料夾:「我的文件」、「控制台」等無實體路徑,SHGDN_FORPARSING 可能失敗,需容錯
  • 效能:大量檔案時可快取 IShellFolder* 避免重複 SHBindToParent
  • 記憶體洩漏:每個 CoTaskMemAlloc 必有對應 CoTaskMemFree

結語

利用 Shell PIDL 機制,一行 GetItemPathName 徹底解決副檔名隱藏導致的檔名歧義。這也是 Windows Shell 程式設計「以 PIDL 為中心」設計哲學的經典體現。原始碼已整理至附件,歡迎直接整合專案。

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