首先介绍一个兼容Unicode和多字节的方法,定义如下头文件:
1 // TString.h; 2 #pragma once 3 #include <string> 4 5 #ifdef UNICODE 6 typedef std::wstring TString; 7 #ifndef _T 8 #define _T(x) L ## x 9 #endif /* END _T */ 10 #else 11 typedef std::string TString; 12 #ifndef _T 13 #define _T(x) x 14 #endif /* END _T */ 15 #endif
下面贴出两种方式的代码。
一般使用方法:
1 bool SelectFilePath( TString& strFilePath ) 2 { 3 TCHAR szPathName[MAX_PATH] = {0}; 4 BROWSEINFO bInfo = {0}; 5 bInfo.hwndOwner = GetForegroundWindow(); // 父窗口; 6 bInfo.lpszTitle = _T("选择目录"); 7 bInfo.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI /*包含一个编辑框 用户可以手动填写路径 对话框可以调整大小之类的..;*/ 8 | BIF_UAHINT /*带TIPS提示*/ /*| BIF_NONEWFOLDERBUTTON 不带新建文件夹按钮*/; 9 // 关于更多的 ulFlags 参考 http://msdn.microsoft.com/en-us/library/bb773205(v=vs.85).aspx; 10 11 LPITEMIDLIST lpDlist; 12 lpDlist = SHBrowseForFolder(&bInfo); 13 if ( nullptr == lpDlist ) // 单击了确定按钮; 14 { 15 strFilePath.clear(); 16 return false; 17 } 18 SHGetPathFromIDList(lpDlist, szPathName); 19 strFilePath = szPathName; 20 return true; 21 }
这样打开的是根目录,每次都定位到根目录中, 很是麻烦。
下面介绍可以设置打开时的默认目录。
1 // 此回调函数为全局函数或静态函数; 2 int CALLBACK BrowseCallbackProc( HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData ) 3 { 4 switch(uMsg) 5 { 6 case BFFM_INITIALIZED: 7 { 8 ::SendMessage(hwnd, BFFM_SETSELECTION, TRUE, (LPARAM)lpData); 9 } 10 break; 11 default: 12 break; 13 } 14 return 0; 15 } 16 17 bool SelectFilePath( TString& strFilePath ) 18 { 19 TCHAR szPathName[MAX_PATH] = {0}; 20 BROWSEINFO bInfo = {0}; 21 TString strDefaultPath = _T("E:\\"); // 注意路径中不要带'\..\'或'\.\'符号,否则设置默认路径失败; 22 bInfo.hwndOwner = GetForegroundWindow(); // 父窗口; 23 bInfo.lpszTitle = _T("选择目录"); 24 bInfo.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI /*包含一个编辑框 用户可以手动填写路径 对话框可以调整大小之类的..;*/ 25 | BIF_UAHINT /*带TIPS提示*/ /*| BIF_NONEWFOLDERBUTTON 不带新建文件夹按钮*/; 26 // 关于更多的 ulFlags 参考 http://msdn.microsoft.com/en-us/library/bb773205(v=vs.85).aspx; 27 bInfo.lpfn = (CDataBackupDlg::BrowseCallbackProc); 28 bInfo.lParam = (LPARAM)(LPCTSTR)(strDefaultPath.c_str()); 29 30 LPITEMIDLIST lpDlist; 31 lpDlist = SHBrowseForFolder(&bInfo); 32 if ( nullptr == lpDlist ) // 单击了确定按钮; 33 { 34 strFilePath.clear(); 35 return false; 36 } 37 SHGetPathFromIDList(lpDlist, szPathName); 38 strFilePath = szPathName; 39 return true; 40 }
注意:目录浏览函数不仅可以选择目录,也可以用来选择一个文件。
详细介绍一下回调函数的相关知识。
//目录浏览对话框可能会像回调函数发送3种消息:
//BFFM_INITIALIZED -- 通知对话框已经初始化结束。
// 回调函数响应此消息时通常是做初始选择
//BFFM_SELCHANGED -- 目录浏览对话框当前选择项发生变化时调用此消息。
// 回调函数响应此消息时通常是显示所选项的相关信息
//BFFM_VALIDATEFAILED -- 表示用户按确认按钮时却发现浏览对话框的编辑框内输入了一个非法名称
// 回调函数响应此消息时通常是提示客户选择项非法,并确定是否继续显示该对话框
//回调函数可以发送如下几个消息给目录浏览对话框,从而改变目录浏览对话框的面目
//BFFM_SETSELECTION -- 改变当前选择项目
//BFFM_ENABLEOK -- 改变“确认”按钮的状态
//BFFM_SETSTATUSTEXT-- 改变目录浏览对话框中状态行消息,当然前提是目录浏览对话框中有状态行
1 int CALLBACK BrowseCallbackProc(HWND hwnd,UINT uMsg,LPARAM lParam,LPARAM lpData) 2 { 3 switch (uMsg) 4 { 5 case BFFM_INITIALIZED: 6 { 7 //BFFM_INITIALIZED表示浏览对话框已经初化结束,参数lParam为NULL 8 //设置初始选项 ::SendMessage(hwnd,BFFM_SETSELECTION,TRUE,lpData); 9 10 //关于BFFM_SETSELECTION消息的说明 11 //wParam :标记lParam参数包含一个ITEMIDLIST结构(PIDL)还是一个目录路径名 12 // 如果为TRUE,lParam内容为路径名;否则lParam包含一个路径PIDL。 13 //lParam :内容为浏览对话框所选的路径。如果wParam为TRUE,lParam内容为一个 14 // 以NULL结尾的字符串的指针,否则为PIDL 15 16 break; 17 } 18 case BFFM_SELCHANGED: 19 { 20 //BFFM_SELCHANGED表示选择项已经发生变化,参数lParam包含列表中最新选中项的条目ID 21 ITEMIDLIST * pidl; 22 char path[MAX_PATH]; 23 //根据条目ID取路径信息 24 pidl = (ITEMIDLIST*) lParam; 25 if (SHGetPathFromIDList(pidl, path)) 26 { 27 //使得“确认”按钮生效 28 //关于BFFM_ENABLEOK消息的说明 29 //wParam :无意义,可设置为0 30 //lParam :如果为非0,则使能确认按钮;否则失效“确认”按钮 31 ::SendMessage(hwnd,BFFM_ENABLEOK,0,TRUE); 32 33 //读属性 34 DWORD attributes = ::GetFileAttributes(path); 35 36 //命令状态行显示当前所选项的全路径名及其文件属性 37 //关于BFFM_SETSTATUSTEXT消息的说明 38 //wParam :无意义,可设置为0 39 //lParam :指向一个内含状态行提示信息的字符串 40 CString strText; 41 strText.Format("%s%s%s%s", 42 path, 43 attributes & FILE_ATTRIBUTE_HIDDEN ? ",H":"", 44 attributes & FILE_ATTRIBUTE_READONLY ? ",R":"", 45 attributes & FILE_ATTRIBUTE_SYSTEM ? ",S":"" 46 ); 47 ::SendMessage(hwnd,BFFM_SETSTATUSTEXT,0,(LPARAM)(LPTSTR)(LPCTSTR)strText); 48 } 49 else 50 { 51 //使得“确认”按钮失效 52 ::SendMessage(hwnd,BFFM_ENABLEOK,0,FALSE); 53 54 //清状态行信息 55 ::SendMessage(hwnd,BFFM_SETSTATUSTEXT,0,(LPARAM)(LPTSTR)(LPCTSTR)""); 56 } 57 break; 58 } 59 case BFFM_VALIDATEFAILED: 60 { 61 //BFFM_VALIDATEFAILED表示用户在浏览对话框的编辑框内输入了一个非法名称 62 //该消息在用户按“确认”时送出——当然前提是编辑框内输入的名称非法 63 //lParam参数包含了非法输入内容的地址,应用程序可以使用这个消息提示用户输入非法。 64 //另外,此消息的回调函数返回0表示目录浏览对话框旋即关闭,返回其他值则允许对话框继续显示。 65 //仅当目录浏览对话框中含有编辑框并且设置了BIF_VALIDATE标记才可能出现此消息 66 //即BROWSEINFO结构中ulFlags含有BIF_EDITBOX|BIF_VALIDATE标志 67 CString strTip; 68 strTip.Format("目录%s非法!",lParam); 69 70 //返回0允许对话框提前关闭,SHBrowseForFolder()返回NULL 71 AfxMessageBox(strTip); 72 return 0; 73 74 //返回1对话框继续显示,因为对话框仍继续显示,可以在状态行显示出错消息 75 //注意:如果此时仍用AfxMessageBox来显示提示信息,提示信息框关闭后,要使焦点重返目录 76 //浏览对话框,需要客户手工移动鼠标激活该对话框才行,这样会使得后继操作不是很方便,所以在状态行显示提示信息比较好 77 //::SendMessage(hwnd,BFFM_SETSTATUSTEXT,0,(LPARAM)(LPTSTR)(LPCTSTR)strTip); 78 //return 1; 79 break; 80 } 81 default: 82 { 83 ASSERT(FALSE); 84 } 85 } 86 return 0; 87 }