Search code examples
winapicomwindows-shell

How can I add a virtual folder(IShellFolder) to the Explorer view from within a namespace extension?


I have a namespace extension in development and I'm stuck adding a folder programmatically from within the extension. What happens visually is a tool bar button is clicked, a file path is chosen and that file is the datasource that the virtual folders and files are created from.

What I want to happen since the load file button on the tool bar is within the namespace extensions root, is for the new virtual folder to appear automatically. In order for it to show up, I need to click the tree view or go up out of the root and back in. Then the folder is present.

I've researched this problem and normally when this occurs people are using SHChangeNotify. I tried that like the example below and using various combonations such as providing the path or pidl off the namespace extension root, the example below including the new folder that should be there, using the pidl of that path (with proper flag in SHChangeNotify) and still no dice. I also tried the SHCNE_xxx where xxx is a notify all flag. SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_IDLIST, retPidl, 0);

Right clicking the view pane and choosing refresh does not invoke the folder to appear.

In the code that gets the path of the folder, I called BindToObject of my folder then EnumObjects and Next, the breakpoints weren't hit. When I click the tree view or go up a folder and back down, all the breakpoints are hit. SHChangeNotify does not invoke the breakpoints.

The view is created using SHCreateShellFolderView in CreateViewObject like so:

if (IID_IShellView == riid) {
    SFV_CREATE csfv = {0};
    csfv.cbSize     = sizeof(SFV_CREATE);
    hr              = QueryInterface(IID_PPV_ARGS(&csfv.pshf));
    if (SUCCEEDED(hr)) {
        hr = CShellFolderView_CreateInstance(IID_PPV_ARGS(&csfv.psfvcb));
        if (SUCCEEDED(hr)) {
            hr = SHCreateShellFolderView(&csfv, (IShellView**)ppv); 
            csfv.psfvcb->Release();
    m_hWnd = hwndOwner;
        }
        csfv.pshf->Release();
    }
}

in the ShellVolderView class, I set a bp on the notification flags and they never hit. I read that returning S_OK is needed for the SFVM_UPDATEOBJECT so I added that.

IFACEMETHODIMP CShellFolderView::MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam) {
    HRESULT hr = E_NOTIMPL;
    switch(uMsg) {
        case SFVM_GETSORTDEFAULTS:
            wParam  = (WPARAM)(int*)-1;
            lParam  = (LPARAM)(int*)Col::Size;
            hr      = S_OK;
            break;
        case SFVM_GETNOTIFY:
            *((LONG*)lParam)    = SHCNE_ALLEVENTS;
            hr                  = S_OK;
            break;
    }
    return hr;
}

*Edit: Adding suspect function. When the *ppidl is NULL, E_INVALIDARG is returned.

STDMETHODIMP CShellFolder::ParseDisplayName(HWND hwnd, IBindCtx *pbc, LPWSTR pszDisplayName, ULONG *pchEaten, PIDLIST_RELATIVE *ppidl, ULONG *pdwAttributes) {
    HRESULT hr = E_INVALIDARG;

    if (pszDisplayName && *ppidl) {
        WCHAR szNameComponent[MAX_SIZE] = {};
        PWSTR pszNext                   = PathFindNextComponent(pszDisplayName);
        if (pszNext && *pszNext) {
            // unsure of max length, can't use StringCchLength for this
            size_t len      = lstrlen(pszDisplayName);
            size_t nextLen  = 0;
            StringCchLength(pszNext, MAX_SIZE, &nextLen);
            hr = StringCchCopyN(szNameComponent, MAX_SIZE, pszDisplayName, len - nextLen);
        }
        else {
            hr = StringCchCopy(szNameComponent, MAX_SIZE, pszDisplayName);
        }
        if (SUCCEEDED(hr)) {
            PathRemoveBackslash(szNameComponent);
            PIDLIST_RELATIVE pidlCurrent    = NULL;
            LPPIDLDATA child                = m_pPidlMgr->GetSubItemFromPidl((LPCITEMIDLIST)ppidl);
            hr                              = m_pPidlMgr->Create(&pidlCurrent, child);
            if (SUCCEEDED(hr)) {
                if (pszNext && *pszNext) {
                    IShellFolder *psf;
                    hr = BindToObject(pidlCurrent, pbc, IID_PPV_ARGS(&psf));
                    if (SUCCEEDED(hr)) {
                        PIDLIST_RELATIVE pidlNext = NULL;
                        hr = psf->ParseDisplayName(hwnd, pbc, pszNext, pchEaten, &pidlNext, pdwAttributes);
                        if (SUCCEEDED(hr)) {
                            *ppidl = ILCombine(pidlCurrent, pidlNext);
                            ILFree(pidlNext);
                        }
                        psf->Release();
                    }
                    ILFree(pidlCurrent);
                }
                else {
                    *ppidl = pidlCurrent;
                }
            }
        }
    }
    return hr;
}

What steps should I be taking to get the folder to show up programmatically?


Solution

  • Try the following:

    SFVM_GETNOTIFY:
      begin
        PDWORD(ALParam)^ := SHCNE_ALLEVENTS;
        Result := S_OK;
      end;