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);
}



你可能感兴趣的:(Win32遍历指定路径下的文件(Shell实现))