Windows 將「桌面」、「我的文件」、「啟動群組」等使用者專屬資料夾抽象化,透過 **CSIDL (Shell 特殊資料夾 ID)** 識別。SHGetSpecialFolderLocation (舊版) / SHGetFolderPath / SHGetKnownFolderPath (現代) 可取得實際檔案系統路徑,避免硬式編碼路徑導致跨版本、多使用者、漫遊設定檔失效。
HRESULT SHGetSpecialFolderLocation(
HWND hwndOwner, // 視窗控制代碼 (可 NULL)
int nFolder, // CSIDL 常數
LPITEMIDLIST *ppidl // 輸出 PIDL (項目 ID 清單)
);
後續需呼叫 SHGetPathFromIDList 將 PIDL 轉為路徑字串,並用 IMalloc::Free 釋放 PIDL。
| CSIDL | 資料夾 | 典型路徑範例 |
|---|---|---|
CSIDL_DESKTOP | 桌面 (虛擬) | 虛擬命名空間根節點 |
CSIDL_DESKTOPDIRECTORY | 桌面實體目錄 | C:\Users\Name\Desktop |
CSIDL_PERSONAL | 我的文件 | C:\Users\Name\Documents |
CSIDL_STARTUP | 啟動群組 | C:\Users\Name\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup |
CSIDL_PROGRAMS | 開始選單程式 | ... |
CSIDL_SENDTO | 傳送到選單 | ... |
CSIDL_RECENT | 最近使用文件 | ... |
CSIDL_FONTS | 字型資料夾 | C:\Windows\Fonts |
CSIDL_TEMPLATES | 範本 | ... |
CSIDL_BITBUCKET | 資源回收筒 | 虛擬 |
CSIDL_CONTROLS | 控制台 | 虛擬 |
CSIDL_DRIVES | 我的電腦 | 虛擬 |
CSIDL_NETHOOD | 網路芳鄰 | 虛擬 |
CSIDL_PRINTERS | 印表機 | 虛擬 |
#include
#include // SHGetPathFromIDList
#pragma comment(lib, "shell32.lib")
void __fastcall TForm1::Button1Click(TObject *Sender)
{
LPITEMIDLIST pidl = NULL;
LPMALLOC pMalloc = NULL;
char szPath[MAX_PATH] = {0};
// 1. 取得 Shell 記憶體配置器
if (SUCCEEDED(SHGetMalloc(&pMalloc)))
{
// 2. 取得桌面目錄 PIDL (CSIDL_DESKTOPDIRECTORY)
if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOPDIRECTORY, &pidl)))
{
// 3. PIDL 轉路徑
if (SHGetPathFromIDList(pidl, szPath))
{
Label1->Caption = szPath; // 顯示: C:\Users\Name\Desktop
}
// 4. 釋放 PIDL
pMalloc->Free(pidl);
}
// 5. 釋放配置器
pMalloc->Release();
}
}
// SHGetFolderPath (支援 CSIDL, 內部封裝 SHGetKnownFolderPath)
SHGetFolderPath(NULL, CSIDL_DESKTOPDIRECTORY, NULL, SHGFP_TYPE_CURRENT, szPath);
// SHGetKnownFolderPath (使用 KNOWNFOLDERID GUID, 更精確、支援重導向)
PWSTR pszPath = NULL;
SHGetKnownFolderPath(FOLDERID_Desktop, 0, NULL, &pszPath);
// 使用後 CoTaskMemFree(pszPath);
CSIDL_BITBUCKET (回收筒)、CSIDL_CONTROLS (控制台) 等**無實體檔案路徑**,SHGetPathFromIDList 會失敗回傳 FALSEC:\Users\... 會失效,**務必透過 API 取得**CSIDL_COMMON_STARTMENU 共用開始選單)SHGetKnownFolderPath 直接取得 PWSTR,避免 ANSI/Unicode 轉換問題