How to register for File Dialog notifications from Shell namespace extension

If you are implementing a shell namespace extension and want to know when a file system item in your shell namespace extension is chosen by the user from Open Dialog, this blog post is for you.

The basic steps on how to do this as follows:

  1. Implement IObjectWithSite in your COM class that is passed to SHCreateShellFolderView in SFV_CREATE::psfvcb.
  2. In your implementation of IObjectWithSite::SetSite, query IUnknown *pUnkSite for SID_SExplorerBrowserFrame service to retrieve IFileOpenDialog interface.
    Note: it will be NULL if your namespace extension is not hosted by Open Dialog as the interface name suggests.
  3. Implement IFileDialogEvents::OnFileOk in a COM class and pass it to IFileDialog::Advise (IFileOpenDialog is derived from IFileDialog)

OnFileOK will be called when a file system item in your shell namespace extension is chosen by the user from Open Dialog.

Here is how this can be implemented:

class CFolderViewCB : public IShellFolderViewCB,

                        public IFolderViewSettings,

                        public IObjectWithSite

{

public:

    CFolderViewCB() : _cRef(1), m_pUnkSite(NULL), m_cookie(0), m_fileOpenDialog(NULL) {}

 

    // IUnknown

    IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv)

    {

        static const QITAB qit[] =

        {

            QITABENT(CFolderViewCB, IShellFolderViewCB),

            QITABENT(CFolderViewCB, IFolderViewSettings),

            QITABENT(CFolderViewCB, IObjectWithSite),

            { 0 },

        };

        return QISearch(this, qit, riid, ppv);

    }

 

    IFACEMETHODIMP SetSite(IUnknown *pUnkSite);

    IFACEMETHODIMP GetSite(REFIID riid, void **ppvSite);

 

private:

    IUnknown *m_pUnkSite;

    DWORD m_cookie;

    IFileOpenDialog *m_fileOpenDialog;

};

 

 

HRESULT CFolderViewCB::SetSite(IUnknown *pUnkSite)

{

    if (m_pUnkSite != NULL)

    {

        m_pUnkSite->Release();

 

        if (m_fileOpenDialog != NULL)

        {

            m_fileOpenDialog->Unadvise(m_cookie);

            m_fileOpenDialog->Release();

            m_fileOpenDialog = NULL;

            m_cookie = 0;

        }

    }

 

    m_pUnkSite = pUnkSite;

 

    if (m_pUnkSite != NULL)

    {

        m_pUnkSite->AddRef();

 

        HRESULT hr = IUnknown_QueryService(m_pUnkSite, SID_SExplorerBrowserFrame, IID_PPV_ARGS(&m_fileOpenDialog));

        if (SUCCEEDED(hr))

        {

            IFileDialogEvents *fileDialogEvents;

            hr = FileDialogEvents_CreateInstance(IID_PPV_ARGS(&fileDialogEvents));

            if (SUCCEEDED(hr))

            {

                hr = m_fileOpenDialog->Advise(fileDialogEvents, &m_cookie);

 

                fileDialogEvents->Release();

            }

 

            if (FAILED(hr))

            {

                m_fileOpenDialog->Release();

                m_fileOpenDialog = NULL;

            }

        }

 

        if (FAILED(hr))

        {

            //OutputDebugString(L"Failed to subscribe for IFileDialogEvents::Advise\n");

        }

        

    }

 

    return S_OK;

}

 

HRESULT STDMETHODCALLTYPE CFolderViewCB::GetSite(REFIID riid, void **ppvSite)

{

    HRESULT hr = E_FAIL;

 

    if (m_pUnkSite != NULL)

    {

        hr = m_pUnkSite->QueryInterface(riid, ppvSite);

    }

 

    if (FAILED(hr))

    {

        *ppvSite = NULL;

    }

 

    return hr;

}

 

class FileDialogEvents : public IFileDialogEvents

{

public:

    FileDialogEvents() : _cRef(1) {}

 

    // IUnknown

    IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv)

    {

        static const QITAB qit[] =

        {

            QITABENT(FileDialogEvents, IFileDialogEvents),

            { 0 },

        };

        return QISearch(this, qit, riid, ppv);

    }

 

    IFACEMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&_cRef); }

    IFACEMETHODIMP_(ULONG) Release()

    {

        long cRef = InterlockedDecrement(&_cRef);

        if (0 == cRef)

        {

            delete this;

        }

        return cRef;

    }

 

    IFACEMETHODIMP OnFileOk(IFileDialog *pfd)

    {

        PWSTR filePath;

        IShellItem *shellItem;

        if (SUCCEEDED(pfd->GetResult(&shellItem)))

        {

            if (SUCCEEDED(shellItem->GetDisplayName(SIGDN_FILESYSPATH, &filePath)))

            {

                //OutputDebugString(filePath);

                //OutputDebugString(L"\r\n");

                CoTaskMemFree(filePath);

            }

 

            shellItem->Release();

        }

 

        return S_OK;

    }

 

    IFACEMETHODIMP OnFolderChanging(IFileDialog * /*pfd*/, IShellItem * /*psiFolder*/)

    {

        return E_NOTIMPL;

    }

    IFACEMETHODIMP OnFolderChange(IFileDialog * /*pfd*/)

    {

        return E_NOTIMPL;

    }

    IFACEMETHODIMP OnSelectionChange(IFileDialog * /*pfd*/)

    {

        return E_NOTIMPL;

    }

    IFACEMETHODIMP OnShareViolation(IFileDialog * /*pfd*/, IShellItem * /*psi*/, FDE_SHAREVIOLATION_RESPONSE * /*pResponse*/)

    {

        return E_NOTIMPL;

    }

    IFACEMETHODIMP OnTypeChange(IFileDialog * /*pfd*/)

    {

        return E_NOTIMPL;

    }

    IFACEMETHODIMP OnOverwrite(IFileDialog * /*pfd*/, IShellItem * /*psi*/, FDE_OVERWRITE_RESPONSE * /*pResponse*/)

    {

        return E_NOTIMPL;

    }

 

private:

    ~FileDialogEvents()

    {

        

    };

 

    long _cRef;

};

 

HRESULT FileDialogEvents_CreateInstance(REFIID riid, void **ppv)

{

    *ppv = NULL;

 

    HRESULT hr = E_OUTOFMEMORY;

    FileDialogEvents *events = new (std::nothrow) FileDialogEvents();

    if (events)

    {

        hr = events->QueryInterface(riid, ppv);

        events->Release();

    }

    return hr;

}

 

 

Additional references:

IExplorerBrowser interface

https://msdn.microsoft.com/en-us/library/windows/desktop/bb761909(v=vs.85).aspx

Follow us on Twitter, www.twitter.com/WindowsSDK.