1. 用什麼函數
在一些Windows應用程序中,我們會遇到一種對話框,它以樹形列表的形式分層列出所有的磁盤分區和文件夾,並允許我們選擇其中的一個文件夾。這些窗口的外觀都是一致的,所以應該是通過同樣的接口完成的。那麼我們如何在自己的程序中使用文件夾選擇對話框呢。
這是否是Common Dialog Box呢? 可以明確的說--不是,因為無論是GetOpenFileName還是GetSaveFileName,操作的對像都是文件,它們並未提供一個可設置的Flag值來使彈出的對話框可以選擇文件夾。
通過對此類應用程序所調用的繫統DLL函數的分析,會發現在shell.dll中有一個名為SHBrowseForFolder的函數。
在MSDN的Windows Shell Functions的列表裡,我找到這個函數。MSDN對它的解釋是Displays a dialog box that enables the user to select a Shell folder -- 顯示一個對話框以允許用戶選擇一個共享文件夾
。沒錯,就是它。
知道了用什麼函數,那我們快快把它加到代碼裡吧。
2. 函數的說明
2.1 原型
函數的原型式是
LPITEMIDLIST SHBrowseForFolder( LPBROWSEINFO lpbi )
2.2 參數
其中lpbi指向一個BROWSEINFO結構。通過這個結構,我們可以控制對話框的功能和外觀。
BROWSEINFO結構如下
這其中,要注意的參數有這麼幾個。
pszDisplayName
-- 這個參數指向一個緩衝區,SHBrowseForFolder默認這個緩衝區的大小為MAX_PATH(繫統定義的宏,表示一個路徑名的最大長度),並用它來保存被選取的文件夾得路徑。
ulFlags --
SHBrowseForFolder允許我們指定對話框的功能和外觀,這個參數正是起這樣的用途。它的值可以由十多個宏組合而成。比如BIF_RETURNNONLYFSDIRS是隻返回文件繫統的目錄,BIF_BROWSEFORCOMPUTER是隻返回網絡上的電腦名,BIF_EDITBOX是顯示一個編輯框,允許用戶鍵入文件夾名。一般來說,如果是簡單的用於選擇一個文件夾,設置ulFlags = BIF_RETURNONLYLYFSDIRS即可。
pidRoot --
它指向一個ITEMIDLIST結構,作為在列表中顯示的文件夾的根目錄,如果為NULL則默認為Desktop。ITEMIDLIST結構除了用在這裡外,它還用作函數的返回值(參看函數原型)。那麼為什麼不用字符串來表示這個目錄呢,這個結構怎樣表示一個目錄呢?下面作一個簡單的介紹。
2.3 ITEMID和ITEMIDLIST
Windows Shell的一個功能在於管理並提供方法存取繫統中的眾多對像,這些對像包括了文件,網絡上的計算機,控制面板程序,回收站等等,為了識別每一個對像,Windows Shell使用了Item ID來表示它們,而Iten ID Lists用來表示一個對像的路徑。所以,ITEMID和ITEMIDLIST的關繫類似於文件名和路徑的關繫。如果隻對文件繫統而言的話,ITEMIDLIST可以看成是路徑的另一種表示法,Windows Shell也提供了函數來進行轉換。
BOOL SHGetPathFromIDList( LPCITEMIDLIST pidl, LPSTR pszPath )
這個函數將一個ITEMLIST轉換成文件繫統中的路徑。
2.4 返回值
SHBrowseForFolder的返回值也是一個指向ITEMIDLIST的指針。這個ITEMIDLIST正式表示了用戶所選擇的文件夾。如上所述,通過SHGetPathFromIDList,就可以獲得一個文件夾Path的字符串。
要注意的是返回的指針必須由應用程序自己來釋放。並且,由於SHBrowseForFolder是通過調用IMalloc Interface來分配Memory,所以,也必須通過這個Interface來釋放。
3. 完整的例子
在一些Windows應用程序中,我們會遇到一種對話框,它以樹形列表的形式分層列出所有的磁盤分區和文件夾,並允許我們選擇其中的一個文件夾。這些窗口的外觀都是一致的,所以應該是通過同樣的接口完成的。那麼我們如何在自己的程序中使用文件夾選擇對話框呢。
這是否是Common Dialog Box呢? 可以明確的說--不是,因為無論是GetOpenFileName還是GetSaveFileName,操作的對像都是文件,它們並未提供一個可設置的Flag值來使彈出的對話框可以選擇文件夾。
通過對此類應用程序所調用的繫統DLL函數的分析,會發現在shell.dll中有一個名為SHBrowseForFolder的函數。
在MSDN的Windows Shell Functions的列表裡,我找到這個函數。MSDN對它的解釋是Displays a dialog box that enables the user to select a Shell folder -- 顯示一個對話框以允許用戶選擇一個共享文件夾
。沒錯,就是它。
知道了用什麼函數,那我們快快把它加到代碼裡吧。
2. 函數的說明
2.1 原型
函數的原型式是
LPITEMIDLIST SHBrowseForFolder( LPBROWSEINFO lpbi )
2.2 參數
其中lpbi指向一個BROWSEINFO結構。通過這個結構,我們可以控制對話框的功能和外觀。
BROWSEINFO結構如下
typedef struct _browseinfo {
HWND hwndOwner;
// 父窗口句柄
LPCITEMIDLIST pidlRoot;
// 要顯示的文件夾的根(Root)
LPTSTR pszDisplayName;
// 保存被選取的文件夾路徑的緩衝區
LPCTSTR lpszTitle;
// 顯示位於對話框左上部的標題
UINT ulFlags;
// 指定對話框外觀和功能的標志
BFFCALLBACK lpfn;
// 處理事件的回調函數
LPARAM lParam;
// 應用程序傳遞給回調函數的參數
int iImage;
// 保存被選取的文件夾的圖片索引
} BROWSEINFO, *PBROWSEINFO, *LPBROWSEINFO;
HWND hwndOwner;
// 父窗口句柄
LPCITEMIDLIST pidlRoot;
// 要顯示的文件夾的根(Root)
LPTSTR pszDisplayName;
// 保存被選取的文件夾路徑的緩衝區
LPCTSTR lpszTitle;
// 顯示位於對話框左上部的標題
UINT ulFlags;
// 指定對話框外觀和功能的標志
BFFCALLBACK lpfn;
// 處理事件的回調函數
LPARAM lParam;
// 應用程序傳遞給回調函數的參數
int iImage;
// 保存被選取的文件夾的圖片索引
} BROWSEINFO, *PBROWSEINFO, *LPBROWSEINFO;
這其中,要注意的參數有這麼幾個。
pszDisplayName
-- 這個參數指向一個緩衝區,SHBrowseForFolder默認這個緩衝區的大小為MAX_PATH(繫統定義的宏,表示一個路徑名的最大長度),並用它來保存被選取的文件夾得路徑。
ulFlags --
SHBrowseForFolder允許我們指定對話框的功能和外觀,這個參數正是起這樣的用途。它的值可以由十多個宏組合而成。比如BIF_RETURNNONLYFSDIRS是隻返回文件繫統的目錄,BIF_BROWSEFORCOMPUTER是隻返回網絡上的電腦名,BIF_EDITBOX是顯示一個編輯框,允許用戶鍵入文件夾名。一般來說,如果是簡單的用於選擇一個文件夾,設置ulFlags = BIF_RETURNONLYLYFSDIRS即可。
pidRoot --
它指向一個ITEMIDLIST結構,作為在列表中顯示的文件夾的根目錄,如果為NULL則默認為Desktop。ITEMIDLIST結構除了用在這裡外,它還用作函數的返回值(參看函數原型)。那麼為什麼不用字符串來表示這個目錄呢,這個結構怎樣表示一個目錄呢?下面作一個簡單的介紹。
2.3 ITEMID和ITEMIDLIST
Windows Shell的一個功能在於管理並提供方法存取繫統中的眾多對像,這些對像包括了文件,網絡上的計算機,控制面板程序,回收站等等,為了識別每一個對像,Windows Shell使用了Item ID來表示它們,而Iten ID Lists用來表示一個對像的路徑。所以,ITEMID和ITEMIDLIST的關繫類似於文件名和路徑的關繫。如果隻對文件繫統而言的話,ITEMIDLIST可以看成是路徑的另一種表示法,Windows Shell也提供了函數來進行轉換。
BOOL SHGetPathFromIDList( LPCITEMIDLIST pidl, LPSTR pszPath )
這個函數將一個ITEMLIST轉換成文件繫統中的路徑。
2.4 返回值
SHBrowseForFolder的返回值也是一個指向ITEMIDLIST的指針。這個ITEMIDLIST正式表示了用戶所選擇的文件夾。如上所述,通過SHGetPathFromIDList,就可以獲得一個文件夾Path的字符串。
要注意的是返回的指針必須由應用程序自己來釋放。並且,由於SHBrowseForFolder是通過調用IMalloc Interface來分配Memory,所以,也必須通過這個Interface來釋放。
3. 完整的例子
BOOL SelectFolder( HWND hWnd, LPSTR lpszFolder )
{
BROWSEINFO bi;
ITEMIDLIST *pidl;
TCHAR szPath[MAX_PATH];
memset( &bi, 0, sizeof(bi) );
bi.hwndOwner = hWnd;
bi.pidlRoot = NULL;
bi.pszDisplayName = szPath;
bi.lpszTitle = TEXT("Please select a folder");
bi.ulFlags = BIF_RETURNONLYFSDIRS;
bi.lpfn = NULL;
bi.lParam = 0;
bi.iImage = 0;
pidl = SHBrowseForFolder( &bi );
if( pidl )
{
SHGetPathFromIDList( pidl, lpszFolder );
LPMALLOC lpMalloc;
if( SUCCEEDED(SHGetMalloc(&lpMalloc)) )
{
lpMalloc->Free( pidl );
lpMalloc->Release();
}
return TRUE;
}
return FALSE;
}
{
BROWSEINFO bi;
ITEMIDLIST *pidl;
TCHAR szPath[MAX_PATH];
memset( &bi, 0, sizeof(bi) );
bi.hwndOwner = hWnd;
bi.pidlRoot = NULL;
bi.pszDisplayName = szPath;
bi.lpszTitle = TEXT("Please select a folder");
bi.ulFlags = BIF_RETURNONLYFSDIRS;
bi.lpfn = NULL;
bi.lParam = 0;
bi.iImage = 0;
pidl = SHBrowseForFolder( &bi );
if( pidl )
{
SHGetPathFromIDList( pidl, lpszFolder );
LPMALLOC lpMalloc;
if( SUCCEEDED(SHGetMalloc(&lpMalloc)) )
{
lpMalloc->Free( pidl );
lpMalloc->Release();
}
return TRUE;
}
return FALSE;
}