使用SHBrowseForFolder函數打開文件目錄對話框
自從第一份工作辭職后,差不多三年多沒有再搞MFC了,對于微軟的Windows桌面開發真的淡忘了,這次接到一個任務,需要開發個小工具,雖然任務很小,但是在一個如何打開文件目錄對話框的小問題上卡了下,雖然通過網絡找到了解決方法,但是網上七拼八湊的文章太多,看的有點費時,所以就抽時間小結一下,雖然也是簡單使用,沒有太多擴展,就當給自己Mark一下吧。
關于SHBrowseForFolder函數和簡單使用
打開文件目錄對話框,我找到的方法就是使用SHBrowseForFolder函數,這個函數的原型是LPITEMIDLIST SHBrowseForFolder(LPBROWSEINFO lpbi)。函數很簡單,就一個返回值和一個參數。參數簡單羅列如下
typedef struct _browseinfo {
HWND hwndOwner; // 父窗口句柄
LPCITEMIDLIST pidlRoot; // 要顯示的文件目錄對話框的根(Root)
LPTSTR pszDisplayName; // 保存被選取的文件夾路徑的緩沖區
LPCTSTR lpszTitle; // 顯示位于對話框左上部的標題
UINT ulFlags; // 指定對話框的外觀和功能的標志
BFFCALLBACK lpfn; // 處理事件的回調函數
LPARAM lParam; // 應用程序傳給回調函數的參數
int iImage; // 文件夾對話框的圖片索引
} BROWSEINFO, *PBROWSEINFO, *LPBROWSEINFO
一般而言父窗口句柄(hwndOwner)和根(pidlRoot)設置為Null就可以了,pszDisplayName設定一塊MAX_PATH大小的緩沖區,跟顯示相關的參數就是對話框提示標題(lpszTitle)、對話框樣式(ulFlags)、設定對話框的缺省路徑的操作(lpfn和lParam)以及對話框任務欄上顯示的圖標(iImage)。
由于返回值LPITEMIDLIST是一個指向ITEMIDLIST的指針,這個ITEMIDLIST涉及到Windows Shell中關于管理諸如文件、網絡上的計算機、控制面板程序、回收站等等對象的知識點,Windows Shell為了識別具體的每一個對象,就使用了ITEMID來唯一識別和區分,而ITEMIDLIST就是一個完整的對象路徑。顯然這個函數可以用來瀏覽非文件對象,比如局域網內的電腦等等,在這里這個LPITEMIDLIST返回的對象路徑是一個文件夾的路徑,Windows提供了一個函數BOOL SHGetPathFromIDList(LPCITEMIDLIST pidl, LPSTR pszPath)來實現從對象路徑轉化為文件夾路徑。
在這里需要注意的是,這個返回值是通過調用IMalloc Interface來分配內存的,函數并不負責釋放內存操作,所以我們在使用完這個返回值之后,必須通過IMalloc Interface來釋放內存。
下面給出一段最簡單的使用代碼
BROWSEINFO bi;
char Buffer[MAX_PATH];
//初始化入口參數bi開始
bi.hwndOwner = NULL;
bi.pidlRoot =NULL;//初始化制定的root目錄很不容易
bi.pszDisplayName = Buffer;//此參數如為NULL則不能顯示對話框
bi.lpszTitle = "選擇Sis目標文件路徑";
bi.ulFlags = BIF_EDITBOX;//帶編輯框的風格
bi.lpfn = NULL;
bi.lParam = 0;
bi.iImage=IDR_MAINFRAME;
//初始化入口參數bi結束
LPITEMIDLIST pIDList = SHBrowseForFolder(&bi);//調用顯示選擇對話框
if(pIDList)
{
SHGetPathFromIDList(pIDList, Buffer);
//取得文件夾路徑到Buffer里
CString m_cSisDes = Buffer;//將路徑保存在一個CString對象里
}
UpdateData(FALSE);
// free memory used
IMalloc * imalloc = 0;
if (SUCCEEDED(SHGetMalloc(&imalloc)))
{
imalloc->Free (pIDList);
imalloc->Release();
}
如上代碼可以顯示一個最簡單的帶編輯框的其實選中對象為我的電腦的瀏覽文件夾對話框。
創建一個可以新建文件夾且指定選中初始路徑的瀏覽文件夾對話框
由于我在實際工作中需要的就是一個有新建文件夾功能且指定初始選中路徑的瀏覽文件夾對話框,就把這個需求當做擴展應用吧,由于對話框樣式由ulFlags標記確定,而在系統頭文件SHLOBJ.h頭文件中給出的對話框樣式只有如下幾種
// Browsing for directory.
#define BIF_RETURNONLYFSDIRS 0x0001 // For finding a folder to start document searching
#define BIF_DONTGOBELOWDOMAIN 0x0002 // For starting the Find Computer
#define BIF_STATUSTEXT 0x0004
#define BIF_RETURNFSANCESTORS 0x0008
#define BIF_EDITBOX 0x0010
#define BIF_VALIDATE 0x0020 // insist on valid result (or CANCEL)
#define BIF_BROWSEFORCOMPUTER 0x1000 // Browsing for Computers.
#define BIF_BROWSEFORPRINTER 0x2000 // Browsing for Printers
#define BIF_BROWSEINCLUDEFILES 0x4000 // Browsing for Everything
沒有滿足我需求的樣式,通過csdn查到其實有一個支持新建文件夾功能的樣式值0x40,通常網絡上給出宏為BIF_NEWDIALOGSTYLE和BIF_USENEWUI,由于不知道在具體哪個頭文件中,所以我們可以在代碼中自己定義一下這兩個宏,具體如下
#define BIF_NEWDIALOGSTYLE 0x40
#define BIF_USENEWUI (BIF_NEWDIALOGSTYLE|BIF_EDITBOX)
這樣一來第一個問題解決了,那么如何讓對話框有初始選中的文件夾路徑呢,我起初想著通過pidlRoot,結果撞了一鼻子灰,原來設定初始選中文件夾路徑,是通過那個神奇的回調函數來實現,換句話來說你調用SHBrowseForFolder也就好比你調用了CDialog:: DoModal()函數,具體這個對話框里面的類似初始化,選擇等操作的不同實現就通過lpfn這個回調函數來實現了。
下面給出這個簡單擴展的代碼
#define BIF_NEWDIALOGSTYLE 0x40
int CALLBACK BrowseCallbackProc(HWND hwnd,UINT uMsg,LPARAM lParam,LPARAM lpData)
{
if(uMsg == BFFM_INITIALIZED)
{
SendMessage(hwnd, BFFM_SETSELECTION, TRUE, lpData);
}
return 0;
}
void CSisAppendMidDlg::OnButtonSisdes()
{
// TODO: Add your control notification handler code here
BROWSEINFO bi;
char Buffer[MAX_PATH];
//初始化入口參數bi開始
bi.hwndOwner = NULL;
bi.pidlRoot =NULL;//初始化制定的root目錄很不容易
bi.pszDisplayName = Buffer;//此參數如為NULL則不能顯示對話框
bi.lpszTitle = "選擇Sis目標文件路徑";
bi.ulFlags = BIF_EDITBOX|BIF_NEWDIALOGSTYLE;
CFileFind finder;
if(finder.FindFile(m_cSisDes)==FALSE)
{
bi.lParam =0;
bi.lpfn = NULL;
}
else
{
bi.lParam = (long)(m_cSisDes.GetBuffer(m_cSisDes.GetLength()));//初始化路徑,形如(_T("c:\\Symbian"));
bi.lpfn = BrowseCallbackProc;
}
finder.Close();
bi.iImage=IDR_MAINFRAME;
//初始化入口參數bi結束
LPITEMIDLIST pIDList = SHBrowseForFolder(&bi);//調用顯示選擇對話框
if(pIDList)
{
SHGetPathFromIDList(pIDList, Buffer);
//取得文件夾路徑到Buffer里
m_cSisDes = Buffer;//將路徑保存在一個CString對象里
}
UpdateData(FALSE);
// free memory used
IMalloc * imalloc = 0;
if ( SUCCEEDED(SHGetMalloc( &imalloc)))
{
imalloc->Free (pIDList);
imalloc->Release();
}
}
好了,簡單擴展就到這里為止,至于很多大神需要再做更深一步的擴展,那就沿著這個思路走下去就可以了。畢竟我目前也就走到這一步了,呵呵。
posted on 2010-12-30 18:35
frank.sunny 閱讀(18774)
評論(0) 編輯 收藏 引用 所屬分類:
MFC相關技術