Win32遍歷指定路徑下的文件(Shell實現)

    其實有很多種方法實現遍歷指定路徑下的文件,最普通的可能就是用FindFirstFile、FindNextFile等API來實現,這種實現方法也可以,但是,如果文件夾時裏面有子文件夾的話,用這個方法實現起來就有點麻煩,可能要用遞歸,而遞歸這種方式效率是個很大的問題,在這不打算講這種方法,下面要說的是另一種方式------Windows Shell。

    其實用Shell來實現,思路很簡單,先通過某種方式得到對應路徑的PIDL,然後再得到與之對應的IShellFolder對象,再用IShellFolder::EnumObjects得到IEnumIDList接口,最終用IEnumIDList接口來遍歷所有的文件。思路是這樣子的,但這裏面還是涉及到了很多關於Shell的概念,在看下面這段代碼之前,需要搞明白,不然看起來可能很費勁。

BOOL GetAllFilesFromFolderPath(IN LPCTSTR lpFolderPath, 
    OUT vector<wstring> &vctFiles)
{
    if ( !PathFileExists(lpFolderPath) )
    {
        return FALSE;
    }

    WCHAR szFolderPath[MAX_PATH] = { 0 };
    IShellFolder *psfDesktop = NULL;
    IShellFolder *psfWorkDir = NULL;
    IEnumIDList  *penumIDList = NULL;
    LPITEMIDLIST  pidworkDir = NULL;
    wcscpy_s(szFolderPath, MAX_PATH, lpFolderPath);

    // +1
    HRESULT hr = SHGetDesktopFolder(&psfDesktop);
    if (SUCCEEDED(hr))
    {
        // +2
        hr = psfDesktop->ParseDisplayName(NULL, NULL, 
            szFolderPath, NULL, &pidworkDir, NULL);
    }

    if (SUCCEEDED(hr))
    {
        // +3
        hr = psfDesktop->BindToObject(pidworkDir, 
            NULL, IID_PPV_ARGS(&psfWorkDir));
    }

    if (SUCCEEDED(hr))
    {
        // +4
        hr = psfWorkDir->EnumObjects(NULL, 
            SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &penumIDList);
    }

    if (SUCCEEDED(hr))
    {
        ULONG celtFetched = 0;
        LPITEMIDLIST pidChild = NULL;
        while (SUCCEEDED(penumIDList->Next(1, &pidChild, &celtFetched))
            && (1 == celtFetched))
        {
            // Get the file path from the PIDL of the item.
            LPITEMIDLIST pRealIDL = NULL;
            HRESULT hr = SHGetRealIDL(psfWorkDir, pidChild, &pRealIDL);
            if (SUCCEEDED(hr))
            {
                STRRET strName;
                hr = psfWorkDir->GetDisplayNameOf(pRealIDL, 
                   SHGDN_FORPARSING, &strName);
                if (SUCCEEDED(hr))
                {
                    WCHAR szName[MAX_PATH] = { 0 };
                    hr = StrRetToBuf(&strName, pRealIDL, szName, MAX_PATH);
                    if (SUCCEEDED(hr))
                    {
                        vctFiles.push_back(szName);
                    }
                }
                CoTaskMemFree(pRealIDL);
            }

            CoTaskMemFree(pidChild);
        }
    }

    // -4
    SAFE_RELEASE(penumIDList);
    // -3
    SAFE_RELEASE(psfWorkDir);
    // -2
    CoTaskMemFree(pidworkDir);
    // -1
    SAFE_RELEASE(psfDesktop);

    return (vctFiles.size() > 0);
}

    對上面的程序簡單說明一下:

    // +1 那句,首先得到桌面所對就的IShellFolder對象,可以通過它得到指定路徑的PIDL。

    // +2 那句,調用ParseDisplayName方法來得到指定路徑的PIDL,注意,該方法第三個參數類型是LPWSTR,不是LPCWSTR,這意味着它內部可能會改這個參數。

    // +3 那句,通過ShellFolder Bind 一下,得到一個新的ShellFolder,傳入一個PIDL,這個PIDL就是第二步得到來的。

    // +4 那句,枚舉出IEnumIDList接口對象,通過調用它的Next方法,就能取出每一個子元素的PIDL,再通過這個PIDL來得到其對應的路徑。

    // 最後別忘記釋放COM接口,不然就會有內存泄漏。


    下面這一段代碼就是根據一個PIDL得到其路徑,當然,這也可以寫成一個函數,調用方便。

LPITEMIDLIST pRealIDL = NULL;
HRESULT hr = SHGetRealIDL(psfWorkDir, pidChild, &pRealIDL);
if (SUCCEEDED(hr))
{
    STRRET strName;
    hr = psfWorkDir->GetDisplayNameOf(pRealIDL, SHGDN_FORPARSING, &strName);
    if (SUCCEEDED(hr))
    {
        WCHAR szName[MAX_PATH] = { 0 };
        hr = StrRetToBuf(&strName, pRealIDL, szName, MAX_PATH);
        if (SUCCEEDED(hr))
        {
            vctFiles.push_back(szName);
        }
    }
    CoTaskMemFree(pRealIDL);
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章