1

I have my custom shell namespace extension. Just want to have a virtual folder mapped to some folder on disk C:/ with the same functionality.

    using namespace ATL;
    
    class ATL_NO_VTABLE CMyShellFolder :
        public CComObjectRootEx<CComMultiThreadModel>,
        public CComCoClass<CMyShellFolder, &CLSID_CMyShellFolder>,
        public IPersistFolder2,
        public IShellFolder2,
        public IExplorerPaneVisibility
    {
        CComHeapPtr<ITEMIDLIST_ABSOLUTE> m_pidl;
    
        CComPtr<IShellFolder2> m_folder;
    
        CComPtr<IThumbnailHandlerFactory> m_thFactory;
        CComPtr<IUnknown> m_site;
    
    public:
        CMyShellFolder()
        {
        }
    
        static HRESULT WINAPI UpdateRegistry(BOOL reg) throw();
    
        BEGIN_COM_MAP(CMyShellFolder)
            COM_INTERFACE_ENTRY(IShellFolder)
            COM_INTERFACE_ENTRY(IShellFolder2)
            COM_INTERFACE_ENTRY2(IPersist, IPersistFolder)
            COM_INTERFACE_ENTRY(IPersistFolder)
            COM_INTERFACE_ENTRY(IPersistFolder2)
            COM_INTERFACE_ENTRY(IExplorerPaneVisibility)
        END_COM_MAP()
    
        DECLARE_PROTECT_FINAL_CONSTRUCT()

---------------------------------

    HRESULT CMyShellFolder::BindToObject(PCUIDLIST_RELATIVE pidl, IBindCtx* pbc, REFIID riid, LPVOID* ppv)

{
    if (riid == __uuidof(IShellFolder3))
        return E_NOINTERFACE;


    HR;
    CComObject<CMyShellFolder>* folder = nullptr;
    CHECKARG(pidl);
    hr = E_NOINTERFACE;

    if (riid == IID_IShellFolder ||
        riid == IID_IShellFolder2)
    {
        // check it's a folder
        SFGAOF atts = SFGAO_FOLDER;
        auto hr2 = GetAttributesOf(1, (PCUITEMID_CHILD_ARRAY)&pidl, &atts);
        if (FAILED(hr2) || !(atts & SFGAO_FOLDER))
            goto cleanup; // nope, get out

        CHECKHR(CreateInstanceAddRef(&folder));
        CHECKHR(folder->_Initialize(this, pidl));
        CHECKHR(folder->QueryInterface(riid, ppv));

    }

    RELEASE(folder);
    HRONFAIL(L"CMyShellFolder::BindToObject");
}


    HRESULT CMyShellFolder::CreateViewObject(HWND hwnd, REFIID riid, LPVOID* ppv)
    {
    HR;
    SFV_CREATE sfvc = { 0 };
    DEFCONTEXTMENU dcm = { 0 };
    CHECKITEM;
    CHECKARG(ppv);
    hr = E_NOINTERFACE;

    if (riid == IID_IShellView)
    {
        sfvc.cbSize = sizeof(SFV_CREATE);
        CHECKHR(QueryInterface(IID_PPV_ARGS(&sfvc.pshf)));
        QueryInterface(IID_PPV_ARGS(&sfvc.psfvcb));
        CHECKHR(SHCreateShellFolderView(&sfvc, (IShellView**)ppv));
        goto cleanup;
    }

    if (riid == IID_IContextMenu)
    {
        dcm.hwnd = hwnd;
        //dcm.pidlFolder = (PCIDLIST_ABSOLUTE)m_pidl.m_pData;
        QueryInterface(IID_PPV_ARGS(&dcm.pcmcb));
        CHECKHR(QueryInterface(IID_PPV_ARGS(&dcm.psf)));
        CHECKHR(SHCreateDefaultContextMenu(&dcm, riid, ppv));
        goto cleanup;
    }

    if (riid == IID_ITransferSource || // for delete & file operations
        riid == IID_IDropTarget) // for copy paste & dd
    {
        CHECKHR(m_folder->CreateViewObject(hwnd, riid, ppv));
        goto cleanup;
    }
    CHECKHR(m_folder->CreateViewObject(hwnd, riid, ppv));

cleanup:
    RELEASE(dcm.pcmcb);
    RELEASE(dcm.psf);
    RELEASE(sfvc.psfvcb);
    RELEASE(sfvc.pshf);
    HRONFAIL(L"CMyShellFolder::CreateViewObject");
}

From the first site everything works fine, but then I noticed a few unclear moments:

  1. Context menu is not the same. "New" and "Properties" are missed. Pure context menu

  2. Can't rename folder. New name is not applied.

I tried to create CM in different way:

  {
    HKEY result;
    LSTATUS st = RegOpenKeyEx(HKEY_CLASSES_ROOT, L"Directory\\Background", NULL, KEY_QUERY_VALUE, &result); // just check if it exists

    if (st == ERROR_SUCCESS)
    {
      aKeys[cKeys] = result;
      cKeys++;
    }
  }

  DEFCONTEXTMENU dcm =
  {
    hwndOwner,
    NULL,
    NULL, // _spidl,
    static_cast<IShellFolder2 *>(this),
    cidl,
    rgpidl,
    NULL,
    cKeys,
    aKeys
  };

  hr = SHCreateDefaultContextMenu(&dcm, riid, ppv);

And "New" item appeared(still no "Properties") but didn't work and leaded explorer to crash.

  • 1
    "Properties" requires the item in question to have the SFGAO_HASPROPSHEET attribute set. "New" is more complex but requires the containing folder to have SFGAO_STORAGEANCESTOR and possibly SFGAO_STORAGE set. Rename requires SFGAO_CANRENAME and IShellFolder::SetNameOf to be implemented properly. As for images, you should post another question, don't group questions in one post – Simon Mourier May 11 '22 at 13:32
  • @SimonMourier , all this flags are set. val Attributes = d &Hb8800177 a tried many different combinations but it doesn't help. as for SetNameOf - that could be the case. Thanks – Pavel Eroshenko May 13 '22 at 07:56
  • This flags are mandatory for the Shell to even try, but having them set doesn't mean everything works. Most of commands will not work or even appear for example if you implement IContextMenuCB (not mandatory) improperly. Plus I don't know what are these flags you show, I'm talking about the flags IShellFolder::GetAttributesOf returns for a given shell item. Writing Shell Namespace Extension is a complex task. Put a small reproducing project somewhere for further analysis. – Simon Mourier May 13 '22 at 10:59
  • @SimonMourier. I trued to understand what is needed to implement to support new or properties item, but all I found was about this SFGAO flags. msdn documentation is not really helpful. https://bitbucket.org/erosh/simpleshellextension/src/master/ Here is the sample project. – Pavel Eroshenko May 18 '22 at 09:27
  • Ok, you're talking about the Shell view context menu (the one displayed when no item or folder is selected), not the Shell items' context menu. In your case, since you just wrap an existing folder, just replace the manual handling of IContextMenu in `CreateViewObject` (`if (riid == IID_IContextMenu || riid == IID_IContextMenu2) etc..`) and delegate to the folder (`CHECKHR(m_folder->CreateViewObject(hwnd, riid, ppv));`) – Simon Mourier May 19 '22 at 05:55
  • @SimonMourier, it didn't help `HRESULT CMyShellFolder::CreateViewObject(HWND hwnd, REFIID riid, LPVOID* ppv) { HR; CHECKHR(m_folder->CreateViewObject(hwnd, riid, ppv)); cleanup: HRONFAIL(L"CMyShellFolder::CreateViewObject");` and I don't understand why it should. I had the default implementation for IContextMenu and ISheelView. – Pavel Eroshenko May 20 '22 at 17:57
  • Yes it helps, I tested it. I didn't say you should replace everything, just the query for IContextMenu https://pastebin.com/raw/zrN3MfWh – Simon Mourier May 20 '22 at 18:31
  • @SimonMourier thank you, it really works. I was just registering the old library. In fact, if you do exactly the same for IID_IShellView, then the problem with opening images and having to force refresh after any action will disappear. But I want to understand why previous code didn't work. What is the difference? https://learn.microsoft.com/ is not very informative. Any other resources you suggest? – Pavel Eroshenko May 23 '22 at 07:50
  • It's just because the implementation of IShellFolder and friends (this is done by windows.storage.dll) that the code you have wraps, is specific and not especially documented, so it's hard to reproduce but when you only wraps existing shell items, you can defer most of the code to it blindly. – Simon Mourier May 23 '22 at 07:56
  • @SimonMourier ok. When I pass control of creating an IShellView or IContextMenu to m_folder, it looks much better. But in this case, as I understand it, I lose control of all the actions and can't refresh, for example, TreeView of my shell namespace extension after a folder drag and drom or new folder creation. What is the easiest way to have all the necessary callbacks and pass all the other stuff to the standard windows implementation? – Pavel Eroshenko May 24 '22 at 17:53
  • There's no "easy way". It always depends exactly on the details of what you want to achieve. The Shell uses hundreds of interfaces (some half documented, some not documented at all, etc.) to do what it does. – Simon Mourier May 24 '22 at 18:40

0 Answers0