Search code examples
c++windowswinapiwindows-shellwindows-explorer

Make existing instance of Windows Explorer navigate to specific folder


Detailed context

My project is about creating an icon in Windows Explorer to access a remote location and make navigation on Windows the same as it would be for a local filesystem.

This is working fine, including Adding and Removing an icon in Windows Explorer when the location is not available anymore. However, if Windows Explorer was open on this location before the icon was removed, it keeps displaying it and any interaction there results in an error.

My goal is to make sure that this case doesn't happen : if the location is removed, make any Windows Explorer instance opened to the soon-to-be-removed location navigate to a default location (User directory for example).

What is already done

Accessing the opened Windows Explorer instances is working, as getting the paths of the location they are opened on. I followed these instructions and this is working fine.

For reference, here is my code so far :

std::vector<IWebBrowserApp*> getOpenWindowsExplorers()
{
    std::vector<IWebBrowserApp*> openExplorers;
    IShellWindows *psw;
    if (SUCCEEDED(CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_ALL,
                                   IID_IShellWindows, (void**)&psw))) {
        VARIANT v;
        V_VT(&v) = VT_I4;
        IDispatch  *pdisp;
        BOOL fFound = FALSE;
        for (V_I4(&v) = 0; !fFound && psw->Item(v, &pdisp) == S_OK;V_I4(&v)++)
        {
            IWebBrowserApp *pwba;
            if (SUCCEEDED(pdisp->QueryInterface(IID_IWebBrowserApp, (void**)&pwba)))
            {
                openExplorers.push_back(pwba);
            }
            pdisp->Release();
        }
        psw->Release();
    }
    return openExplorers;
}

QString getCurrentOpenFolder(IWebBrowserApp* explorer)
{
    QString folderPath;

    IServiceProvider *psp;
    if (SUCCEEDED(explorer->QueryInterface(IID_IServiceProvider, (void**)&psp)))
    {
        IShellBrowser *psb;
        if (SUCCEEDED(psp->QueryService(SID_STopLevelBrowser, IID_IShellBrowser, (void**)&psb)))
        {
            IShellView *psv;
            if (SUCCEEDED(psb->QueryActiveShellView(&psv)))
            {
                IFolderView *pfv;
                if (SUCCEEDED(psv->QueryInterface(IID_IFolderView, (void**)&pfv)))
                {
                    IPersistFolder2 *ppf2;
                    if (SUCCEEDED(pfv->GetFolder(IID_IPersistFolder2, (void**)&ppf2)))
                    {
                        LPITEMIDLIST pidlFolder;
                        if (SUCCEEDED(ppf2->GetCurFolder(&pidlFolder)))
                        {
                            TCHAR g_szPath[MAX_PATH];
                            if (SHGetPathFromIDList(pidlFolder, g_szPath))
                            {
                                folderPath = QString::fromWCharArray(g_szPath);
                            }
                            CoTaskMemFree(pidlFolder);
                        }
                        ppf2->Release();
                    }
                    pfv->Release();
                }
                psv->Release();
            }
            psb->Release();
        }
        psp->Release();
    }
    return folderPath;
}

However I can't find any documentation, official or not, on how to set the path of Windows Explorer.

What I found so far

  • IPersistFolder has an Initialize() method that seems to do that. However it takes a PCIDLIST argument and I don't know how to get a PCIDLIST that would correspond to the user profile path, or any other hardcoded path. Also, the official documentation doesn't say anything about how to use it.
  • There is a documentation around IWebBrowser::Navigate() method that also seems to do what I want. However the documentation is not official and there is no explanation on what are the parameters. The method exists though, but official documentation doesn't seem to reference it.

What is the best way to achieve what I want?


Solution

  • My final solution was based on @IInspectable comments :

    void setFolderToDefault(IWebBrowser2 *explorer)
    {
        PIDLIST_ABSOLUTE defaultFolderPidl;
        if (SUCCEEDED(SHGetKnownFolderIDList(FOLDERID_Documents, 0, NULL, &defaultFolderPidl)))
        {
            IServiceProvider *psp;
            if (SUCCEEDED(explorer->QueryInterface(IID_IServiceProvider, (void**)&psp)))
            {
                IShellBrowser *psb;
                if (SUCCEEDED(psp->QueryService(SID_STopLevelBrowser, IID_IShellBrowser, (void**)&psb)))
                {
                    psb->BrowseObject(defaultFolderPidl, SBSP_SAMEBROWSER);
                    psb->Release();
                }
                psp->Release();
            }
            ILFree(defaultFolderPidl);
        }
    }