今天完成一個需求,就是記住用戶選擇的文件路徑,先是熟悉代碼,然后在網上找解決方法,一開始感覺沒什么,網上的方法差不多,應該很容易做出來,結果真是卡了一半天,到晚上自己才慢慢的搞清楚了。
遇到的問題真不少,記錄一下好多細節,真是不寫不知道。
2016-08-18 21:35:36補充:
1.基本方法
http://blog.csdn.net/shuilan0066/article/details/7302904
http://www.cnblogs.com/Hisin/archive/2012/02/27/2370614.html
這兩篇是比較清楚的。
先認識了
SHBrowseForFolder打開文件夾時,每次都是從根目錄打開。要記住上次的路徑或者設置默認路徑,需要寫個回掉函數。
CFileDialog會自動記住上次路徑
int CALLBACK BrowseCallBackFun(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData) { switch(uMsg) { case BFFM_INITIALIZED: //選擇文件夾對話框初始化 //設置默認路徑為lpData即'D:\' ::SendMessage(hwnd, BFFM_SETSELECTION, TRUE, lpData); //在STATUSTEXT區域顯示當前路徑 ::SendMessage(hwnd, BFFM_SETSTATUSTEXT, 0, lpData); //設置選擇文件夾對話框的標題 ::SetWindowText(hwnd, TEXT("請先設置個工作目錄")); break; case BFFM_SELCHANGED: //選擇文件夾變更時 { TCHAR pszPath[MAX_PATH]; //獲取當前選擇路徑 SHGetPathFromIDList((LPCITEMIDLIST)lParam, pszPath); //在STATUSTEXT區域顯示當前路徑 ::SendMessage(hwnd, BFFM_SETSTATUSTEXT, TRUE, (LPARAM)pszPath); } break; } return 0; }
回掉函數首先搞清楚參數的意義
2.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)來實現從對象路徑轉化為文件夾路徑。
弄清楚每個參數的意義,然后才能按需求設定初值。先太盲目了,只知道套別人的,根本就沒有理解。
LPCITEMIDLIST pidl = NULL; BROWSEINFO bi; bi.hwndOwner = AfxGetMainWnd()->GetSafeHwnd(); bi.pidlRoot = NULL; bi.pszDisplayName = folderName; bi.lpszTitle = _T("請選擇用於保存的文件夾"); bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT;; bi.lpfn = BrowseCallbackProc; //回調函數 bi.iImage = 0; bi.lParam = long(&szPath); //設置默認路徑,傳給回掉函數的參數 pidl = SHBrowseForFolder(&bi); if (pidl) { SHGetPathFromIDList(pidl, szPath); }
3.回掉函數
先看別人說設置全局的靜態變量,然后設置靜態變量和靜態函數,開始編譯通不過,要搞清楚類里面怎么寫這種特殊的靜態變量和函數。
靜態變量初始化,在cpp中,回掉函數的定義.h中static,cpp不需要了。
感覺回掉函數只是發送了一些消息,根本就沒有什么其他的用處,然后有人用lpData設置初始化路徑,每次將選中的路徑賦值給IpData也可以完成功能。
4.記住路徑
在軟件開啟的時候,設置全局變量,每個更新默認的路徑即可,但是重新開啟軟件的時候,采用先寫入注冊表中,然后再讀注冊表的路徑,這樣就解決了問題。也可以寫到ini文件中。
5.CString與TCHAR數組 相互轉換
TCHAR數組轉到CString很簡單:使用CString的Format就行。
TCHAR m_buf[100] = _T("Hello");
CString str;
str.Format(L"%s",m_buf);
現在就來CString轉為TCHAR數組,這個就有點麻煩了。因為網上有很多的解決方案,但是都不怎么理想。我們使用_tcscpy()宏。
CString str = L"sssssss";
TCHAR m_buf[20];
_tcscpy(m_buf, str); //類型之間的轉換真麻煩,知道定義這個宏的好處了。
自己找了半天,這個方法很有效!用memcpy,strcpy都不行。。。
TCHAR szDefaultDir[MAX_PATH]; CString strDef(_T("E:\\")); memcpy(szDefaultDir, strDef.GetBuffer(strDef.GetLength() * 2), strDef.GetLength() * 2); strDef.ReleaseBuffer(); szDefaultDir[strDef.GetLength()] = 0;
這樣也是一種方法吧。