1

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?

  • SHCNE_UDPATEDIR says "Hey, if anybody is looking at `thenewfoldername`, they should refresh." But nobody is looking at `thenewfoldername` because it doesn't exist. The documentation says, "If a folder has been created, deleted, or renamed, use SHCNE_MKDIR, SHCNE_RMDIR, or SHCNE_RENAMEFOLDER, respectively." – Raymond Chen Nov 22 '16 at 21:20
  • The namespace extension datasource is a tree structure separate from the SHITEMIDLIST. After updating the datasource tree to add another branch off the root and calling SHChangeNotify correctly (thank you), what other steps should I be taking to get/ask Shell to call IShellFolder2::EnumObjects()? – 505HP C6 Z06 Nov 23 '16 at 16:38
  • 1
    Raise one SHCNE_MKDIR for each directory that was created. If you created a sub-subdirectory, then that means raising it twice, once for the first-level directory and once for the second level directory – Raymond Chen Nov 23 '16 at 22:30
  • I did need to use the explicit SCHNE_MKDIR and SCHNE_RMDIR, the SCHNE_UPDATE didn't work as quite a few people are using. Adding and removing directories is working now with a few other fixes. As an aside, I worked in Windows from '97 to '11. I'm flattered to be helped by you. – 505HP C6 Z06 Nov 26 '16 at 01:33

1 Answers1

1

Try the following:

SFVM_GETNOTIFY:
  begin
    PDWORD(ALParam)^ := SHCNE_ALLEVENTS;
    Result := S_OK;
  end;
Denis Anisimov
  • 3,297
  • 1
  • 10
  • 18
  • In SFVM_GETNOTIFY, I'm now setting the flag like you suggested and setting the pidl in lParam, but IShellFolder:EnumObjects nor BindToObject is getting called after issuing a SHChangeNotify. But SFVM_FSNOTIFY is now getting hit. There is no change in the UI, the folder doesn't show w/out using the tree or going up and back into the root folder. – 505HP C6 Z06 Nov 22 '16 at 18:48
  • If you use DefShellView you can try to remove your SFVM_UPDATEOBJECT, SFVM_QUERYFSNOTIFY, SFVM_ADDOBJECT and SFVM_FSNOTIFY handlers. In case of this values just return E_NOTIMPL, because this values should be processed by DefShellView. – Denis Anisimov Nov 23 '16 at 03:54
  • That makes sense. I was blindly saying things are OK w/out doing anything in those cases. Thanks for that. I'm getting closer, but EnumObject still isn't being called after sending the event notification through SHChangeNotify. I think once EnumObjects is called, Explorer will show the new data by calling the Enumerator interface and then I can create the SHITEMIDs. Any ideas? – 505HP C6 Z06 Nov 23 '16 at 16:42
  • I upvote your question, but why you need EnumObjects? – user2120666 Nov 23 '16 at 16:49
  • Shell doesn't know about the new folder in my datasource. I think Shell would need to call EnumObjects to get my enumerator (IEnumIDList). Then Shell would call call IEnumIDList::Next() where I create the SHITEMIDs for the new folder out of my datasource and return them. At least thats how I think it should work, I've never created a namespace extension before. – 505HP C6 Z06 Nov 23 '16 at 17:17
  • Is your m_pidl in line "*((LPITEMIDLIST*)wParam) = m_pidl;" absolute IDList? In my real working NSE I even don`t touch wParam but when I call SHChangeNotify(SHCNE_UPDATEDIR) shell requests new iterator. – Denis Anisimov Nov 23 '16 at 19:17
  • Do you call SHChangeNotify with path of PIDL? Did you already implement ParseDisplayName? – Denis Anisimov Nov 23 '16 at 19:46
  • The m_pidl is absolute, but it does not yet contain the new data/SHITEMIDs. I will remove that line, it was part of the MSDN doc suggestion. Aside from that, after calling SHChangeNotify using the text path option, ParseDisplayName was getting called but I don't return a new PIDLIST_RELATIVE. I need to study this function more, but it looks like I should be creating the new pidl here for the "thenewfoldername". That could be the last bugfix to get the folder to appear automatically. – 505HP C6 Z06 Nov 23 '16 at 23:24
  • ParseDisplayName isn't being called, making changes to it is needed but wont fix the cause. Is there something so obvious its unsaid that I should be doing to invoke the folders to show up aside from calling SHChangeNotify? – 505HP C6 Z06 Nov 24 '16 at 22:07
  • In my NSE everything is easy. I call SHChangeNotify - shell call IShellFolder.EnumObjects. IMHO NSE technology is very poorly documented and some detail can be clarified only by experiments. In my experiments I use logs and it greatly helps me. All my code looks like this http://stackoverflow.com/questions/30297388/explorer-is-re-navigating-to-the-root-folder-of-my-namespace-extension/30355872#30355872 – Denis Anisimov Nov 25 '16 at 07:08
  • One fix was made to my SHChangeNotify path, I had a missing \ between the two GUIDS (desktop and junction). That fix enabled ParseDisplayName to be called once again. Then I fixed the ParseDisplayName function to create the PIDL of the new folder name and return it. So now the adding folder problem is solved. I appreciate your expertise. – 505HP C6 Z06 Nov 25 '16 at 21:24