Jean-Milost Reymond
Jean-Milost Reymond

Reputation: 1943

C++ - Windows Shell API - Wrong path while iterating through a folder

Consider the following code:

bool ListFolderContent(const std::wstring& fileName)
{
    PIDLIST_ABSOLUTE pidl = nullptr;

    if (FAILED(::SHILCreateFromPath(fileName.c_str(), &pidl, nullptr)))
        return false;

    IShellFolder* pShellfolder = nullptr;
    LPCITEMIDLIST pidlRelative = nullptr;

    HRESULT hr = SHBindToParent(pidl, IID_IShellFolder, (void**)&pShellfolder, &pidlRelative);

    if (FAILED(hr))
    {
        ::CoTaskMemFree(pidl);
        return false;
    }

    IEnumIDList* pEnumIDList = nullptr;

    hr = pShellfolder->EnumObjects(nullptr, SHCONTF_NONFOLDERS, &pEnumIDList);

    if (FAILED(hr))
    {
        pShellfolder->Release();
        ::CoTaskMemFree(pidl);
        return false;
    }

    while (1)
    {
        LPITEMIDLIST pChild = nullptr;

        hr = pEnumIDList->Next(1, &pChild, nullptr);

        if (FAILED(hr))
        {
            pShellfolder->Release();
            ::CoTaskMemFree(pidl);
            return false;
        }

        if (hr == S_FALSE)
            break;

        wchar_t buffer[MAX_PATH + 1];

        if (::SHGetPathFromIDListW(pChild, buffer))
        {
            ::OutputDebugString(buffer);
            ::OutputDebugString(L"\r\n");
        }
    }

    pShellfolder->Release();
    ::CoTaskMemFree(pidl);

    return true;
}

This code works well and logs the content of the folder owning the given file I pass through the fileName parameter.

However I have an issues with this code: Whatever I pass as file name, the logged path is always C:\Users\Admin\Desktop, and NOT the path to the parent folder I'm iterating, as expected. On the other hand the file names are correct.

Can someone explain me what I'm doing wrong?

Upvotes: 0

Views: 155

Answers (1)

Anders
Anders

Reputation: 101764

You are passing just the last component ("filename") here: SHGetPathFromIDListW(pChild, buffer). Since the desktop is the root you are basically asking to get the filesystem path of [Desktop] [Filename] from the namespace.

There are two solutions:

  • pShellfolder->GetDisplayNameOf(pChild, SHGDN_FORPARSING, ...)+StrRetToBuf. This is fast since you already have an instance of the folder interface.

  • Call SHGetPathFromIDList with an absolute (full) pidl. On the pidl from SHILCreateFromPath, call ILCloneFull, ILRemoveLastID and ILCombine(clone, pChild).


HRESULT Example()
{
    WCHAR buf[MAX_PATH];
    GetWindowsDirectory(buf, MAX_PATH);
    PathAppend(buf, L"Explorer.exe");
    
    PIDLIST_ABSOLUTE pidl, pidlFullItem;
    HRESULT hr = SHILCreateFromPath(buf, &pidl, nullptr); // %windir%\Explorer.exe
    if (FAILED(hr)) return hr;

    LPITEMIDLIST pLeaf;
    IShellFolder*pShellfolder; // %windir%
    hr = SHBindToParent(pidl, IID_IShellFolder, (void**)&pShellfolder, nullptr);
    if (SUCCEEDED(hr))
    {
        IEnumIDList*pEnum;
        // Method 1:
        hr = pShellfolder->EnumObjects(nullptr, SHCONTF_NONFOLDERS, &pEnum);
        if (SUCCEEDED(hr))
        {
            for (; S_OK == (hr = pEnum->Next(1, &pLeaf, nullptr));)
            {
                STRRET sr;
                hr = pShellfolder->GetDisplayNameOf(pLeaf, SHGDN_FORPARSING, &sr);
                if (SUCCEEDED(hr))
                {
                    hr = StrRetToBuf(&sr, pLeaf, buf, MAX_PATH);
                    if (SUCCEEDED(hr)) wprintf(L"M1: Item: %s\n", buf);
                }
            }
            pEnum->Release();
        }

        // Method 2:
        ILRemoveLastID(pidl); // %windir%\Explorer.exe => %windir%
        hr = pShellfolder->EnumObjects(nullptr, SHCONTF_NONFOLDERS, &pEnum);
        if (SUCCEEDED(hr))
        {
            for (; S_OK == (hr = pEnum->Next(1, &pLeaf, nullptr));)
            {
                pidlFullItem = ILCombine(pidl, pLeaf); // %windir% + Filename
                if (pidlFullItem)
                {
                    hr = SHGetPathFromIDListW(pidlFullItem, buf);
                    if (SUCCEEDED(hr)) wprintf(L"M2: Item: %s\n", buf);
                    ILFree(pidlFullItem);
                }
            }
            pEnum->Release();
        }
        pShellfolder->Release();
    }
    ILFree(pidl);
    return hr;
}

Upvotes: 1

Related Questions