天天看點

Windows Explorer(浏覽)對話框和周遊檔案夾及其子目錄函數(FindFisrFile FindNextFile CloseFind)使用

Windows浏覽視窗就是你在安裝程式的時候經常叫你選擇安裝在哪個目錄裡面的對話框,我們一般使用這個對話框來獲得使用者選擇的目錄。

(一)主要牽扯到的函數是:

PIDLIST_ABSOLUTE SHBrowseForFolder(lpbi)

此函數會調用Windows Explorer(浏覽)對話視窗,如果使用者點選确定的話,會傳回一個PIDLIST_ABSOLUTE(絕對路徑辨別序列)。

1:首先講解傳回值:

PIDLIST_ABSOLUTE是PITEMIDLIST的别稱,之是以别稱主要是起字面說明作用,因為還有PIDLIST_RELATIVE也是PITEMIDLIST。PIDLIST_ABSOLUTE得到的是絕對路徑的辨別序列的記憶體指針。是辨別序列而不是路徑名稱。

其中PITEMIDLIST是結構體ITEMIDLIST的指針,而ITEMIDLIST裡面隻有一個結構體成員變量SHITEMID,是以也是一個别名。而SHITEMID的結構體如下:

typedef struct _SHITEMID {

  USHORT cb;

  BYTE abID[1];

} SHITEMID, * LPSHITEMID;

SHITEMID的名稱中,SH是Shell的意思,ITEMID是條目ID,或者對象ID。

因為Shell中使用對象ID(ITEMID)來表示Namespaces Hierarchy(名稱空間層次)中的對象。而Namespace Hierarchy是一個名稱空間層次,就想桌面下的電腦和我的檔案在一個名稱空間,而C:/下的windows和program files在一個名稱空間,同一個名稱空間對象的名稱不能重複,就是為了更好了處理電腦上同名檔案,才引入了名稱空間。

名稱空間對象有兩種,一種是檔案夾(大多數情況),即節點。一種是檔案,即葉子。

一個節點不光可以是檔案夾,也可以是虛拟對象,如列印機連接配接符号(可以連接配接區域網路的列印機),桌面的資源回收筒 (裡面有檔案,但它不是一般的檔案夾目錄)等。

Namespaces Hierarchy中并不使用路徑來表示這些對象,因為他對一些虛拟對象是不适用的,用路徑可以表示某個磁盤上的檔案,方式就是把從根目錄開始,到此檔案的途經過的所有名稱空間層次中的對象名稱連接配接起來,每個對象名稱用'/'間隔開來。但對這樣情況,如使用者的某個檔案夾連接配接指向的是一個遠端伺服器中的一個檔案夾,這是無法用路徑來表示的,它因該是個URL或者其他的方式。總之Namespace Hierarchy提供另一種方式來辨別對象,那就是用ItemID來表征,我們無法從abID這個數組中得到任何可了解的資訊。MSDN告誡我們隻需把Item

ID看做是特定對象的标記就可以。我們經常用PIDLIST來指向一組SHITEMID,每個SHITEMID代表一個對象,每個SHITEMID裡面有一個abID數組,那就相當于我們對象的辨別符,就想名稱一樣。是以一組SHITEMID就相當于我們的路徑了,其中每一個SHITEMID就代表一個相應的檔案夾或者最後的檔案,但是SHITEMID裡面的資料對我們來說是不相幹的,意思就是不是字元串。你無法從表面得知它的含義。是以這裡我們隻需知道PIDLIST相當于路徑辨別符,我們可以通過PIDLIST_ABSOLUTE可以得到一些我們想要的東西。

記住PIDLIST_ABSOLUTE是一個對象ID連結清單指針,指向的ID連結清單是由API函數内部申請的,但其銷毀工作有使用者完成。是以使用者要手動的調用CoTaskMemFree(pidl);

2:講解lpbi

lpbi是BROWSEINFO結構體,其中包含了Windows Explorer對話框的很多有用的資訊,一般設定如下。

  bi.hwndOwner=hWnd;

  bi.pidlRoot=NULL;

  bi.pszDisplayName=szDisplayName;

  bi.lpszTitle=TEXT("選擇你要進行清理的目錄");

  bi.ulFlags=BIF_BROWSEFORCOMPUTER|BIF_RETURNONLYFSDIRS;

  bi.lpfn=NULL;

  bi.lParam=NULL;

  bi.iImage=0;

注意pszDisplayName就是節點名字,也就是檔案夾名稱。ulFlags有很多含義,具體參照MSDN。

其實對我們最重要的是函數的傳回值,就想我上面體的一樣,有了條目ID清單,我們可以通過API函數得到路徑字元串。

(二)要得到路徑辨別序列對應的路徑,我們需要下面這個函數:

BOOL SHGetPathFromIDList(pidl,szPath)

pidl就是你通過SHBrowseForFolder獲得的對象ID連結清單,szPath是一個字元數組,用于儲存函數傳回的路徑;

注意:MSDN中強調我們在用SHBrowseForFolder的時候必須要先調用CoInitializeEx(NULL,COINIT_APARTMENTTHREADED); 

用完後調用CoUninitializeEx();

現在我們講解周遊檔案夾及其子目錄的函數:

hFind FindFirstFile(szFileName,fd);

hFind是檔案查找句柄,通過它可以使用FindNextFile來得到下一個檔案資訊。

szFileName是檔案的完整路徑名,允許使用通配符(?、*)。

fd是WIN32_FIND_DATA結構體。裡面包含了檔案的許多基本資訊。

BOOL FindNextFile(hFind,fd);

一看原型估計就知道什麼意思了。

其中對我們最重要的是dwFileAttribute和cFileName。

dwFileAttribute是告訴我們檔案的屬性,是歸檔還是隐藏,還是隻讀,還是加密了什麼的。

cFileName是檔案的名字。

假設我們要周遊一個檔案夾的檔案及其子目錄的話,例如E:/visual stdio 2008

那麼我們一般将我們要周遊的檔案夾路徑設定為:E:/visual stdio 2008/*

将此字元串傳給FindFirstFind的第一個參數,第二個參數則在成功是儲存檔案資訊。

一般我們找到的第一個對象,我說的是對象,因為我們用*通配符,是以将查找所有的子目錄和檔案。是以我們第一次得到的是對象。第一次查找到的是一個叫做目前目錄檔案夾,也就是cFileName=TEXT(".");的這個檔案夾,這是個系統隐藏檔案夾。下一個一般是cFileName=TEXT("..")的檔案夾,這是上級目錄檔案夾,也是系統隐藏的,除了是在根目錄下,因為根目錄沒有上一級了。在這以後才是我們一般檔案夾裡面看到的檔案。

我們既然說了要周遊所有的子目錄來完成我們的任務,那麼勢必要使用遞歸遞歸,那就是遇到一個子目錄,就将這個目錄全名再次傳給遞歸函數。一遍完成整個目錄子樹的搜尋。

遞歸函數本身負責檔案的處理,和檔案夾的遞歸處理。

下面是一個周遊并删除某個檔案夾下所有vs項目工程裡的一些非必要檔案,以減小磁盤存儲大小。

BOOL Travel(TCHAR pszPath[])

{

static TCHAR* pszFilter[]=//欲删除檔案擴充名

TEXT(".ncb"),

TEXT(".pch"),

TEXT(".pdb"),

TEXT(".htm"),

TEXT(".obj"),

TEXT(".idb"),

TEXT(".dep"),

TEXT(".ilk"),

TEXT(".manifest"),

TEXT(".res"),

TEXT(".suo"),

TEXT(".APS")

};

TCHAR szPath[PATH_LENGTH];//路徑,一般帶有通配符

TCHAR szFileNamePrePath[PATH_LENGTH];//路徑,不帶通配符

TCHAR szFileName[PATH_LENGTH];//完整檔案名

if(FAILED(StringCchCopy(szPath,PATH_LENGTH,pszPath))||//儲存路徑通配符

FAILED(StringCchCopy(szFileNamePrePath,PATH_LENGTH,pszPath)))

MessageBox(NULL,TEXT("Copy Failed!"),szTitle,MB_OK|MB_ICONERROR);

return FALSE;

}

szFileNamePrePath[lstrlen(szFileNamePrePath)-1]=TEXT('/0');//去掉星号

HANDLE hFind=NULL;

WIN32_FIND_DATA fd;

hFind=FindFirstFile(szPath,&fd);

if(INVALID_HANDLE_VALUE==hFind)

do

//找到新的目錄

if(fd.dwFileAttributes==FILE_ATTRIBUTE_DIRECTORY&&

0!=lstrcmp(fd.cFileName,TEXT("."))&&//不是目前目錄

0!=lstrcmp(fd.cFileName,TEXT("..")))//不是上一級目錄

szPath[lstrlen(szPath)-1]=TEXT('/0');

//将路徑設為新的路徑名稱,采用追加的方式

if(FAILED(StringCchCat(szPath,PATH_LENGTH,fd.cFileName))||

FAILED(StringCchCat(szPath,PATH_LENGTH,TEXT("//*"))))

MessageBox(NULL,TEXT("Cat Failed!"),szTitle,MB_OK|MB_ICONERROR);

if(!Travel(szPath))

if(FAILED(StringCchCopy(szPath,PATH_LENGTH,pszPath)))//将路徑值更改回來

else if(0!=lstrcmp(fd.cFileName,TEXT("."))&&0!=lstrcmp(fd.cFileName,TEXT("..")))//找到檔案

TCHAR szExtend[FILEEXTEND];//檔案擴充名字元串,FILEEXTEND=20(自定義)

int iLength=lstrlen(fd.cFileName);

int iIndex=iLength-1;

//獲得擴充名

while(*(fd.cFileName+iIndex)!=TEXT('.'))

iIndex--;

if(FAILED(StringCchCopy(szExtend,FILEEXTEND,fd.cFileName+iIndex)))

//////////////////////////////////////////////////////////////////////////

for(int i=0;i<FILETYPE_NUM;i++)//查找是否找到相關可删除檔案

if(0==lstrcmp(szExtend,pszFilter[i]))

if(FAILED(StringCchCopy(szFileName,PATH_LENGTH,szFileNamePrePath))||

FAILED(StringCchCat(szFileName,PATH_LENGTH,fd.cFileName)))//得到檔案名,第一個函數特别采用copy,防止無限追加産生字元串越界

MessageBox(NULL,TEXT("Cat Failed In Delete"),szTitle,MB_OK|MB_ICONERROR);

break;

if(!DeleteFile(szFileName))//删除不成功

TCHAR szDeleteFailed[PATH_LENGTH+50];

TCHAR szDeleteFormat[PATH_LENGTH];

LoadString(hInst,IDS_DELETE,szDeleteFormat,PATH_LENGTH);

wsprintf(szDeleteFailed,szDeleteFormat,szFileName);

MessageBox(NULL,szDeleteFailed,szTitle,MB_OK|MB_ICONERROR);

} while (FindNextFile(hFind,&fd));

FindClose(hFind);//關閉句柄

return TRUE;

具體的細節不在陳述,如果錯誤,敬請指出。

繼續閱讀