IFileDialog:添加自定义位置 - select 该位置

IFileDialog: Add a Custom Place - and select that place

我正在使用 IFileDialog::AddPlace 添加例如"c:\my\custom\location" 作为自定义位置 select 文件从左侧的导航面板,并将其设置为 (default/forced) 初始文件夹。

但是,当对话框打开时,根驱动器(示例中的 C:)被 selected 而不是自定义位置。

(我使用 SHCreateItemFromParsingName 从路径创建 IShellItem,并在 AddPlaceSetFolder 中使用相同的 Shellitem)

结果:https://imgur.com/w1pZhtd

完整来源:http://pasted.co/17cb14c2

我不确定您是否可以直接从 IFileDialog 界面 select 虚拟文件夹。但是你可以从里面订阅file dialog events and have access to the explorer's left tree view

树视图实现了INameSpaceTreeControl接口

int main()
{
  CoInitialize(NULL);
  {
    LPCWSTR customPath = L"c:\temp"; // use a path that exists...

    CComPtr<IFileDialog> dlg;
    Check(CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&dlg)));

    // subscribe to events
    MyFileDialogEvents* fde = new MyFileDialogEvents();
    DWORD cookie;
    Check(dlg->Advise(fde, &cookie));

    CComPtr<IShellItem> location;
    Check(SHCreateItemFromParsingName(customPath, nullptr, IID_PPV_ARGS(&location)));

    Check(dlg->AddPlace(location, FDAP_TOP));
    Check(dlg->SetFolder(location));
    Check(dlg->Show(0));

    Check(dlg->Unadvise(cookie));
    delete fde;
  }
  CoUninitialize();
  return 0;
}

class MyFileDialogEvents : public IFileDialogEvents
{
  // called when selection has changed
  HRESULT STDMETHODCALLTYPE OnSelectionChange(IFileDialog* pfd)
  {
    // get comdlg service provider
    CComPtr<IServiceProvider> sp;
    Check(pfd->QueryInterface(&sp));

    // get explorer browser
    // note this call would fail if we call it from IFileDialog* directly instead of from an event
    CComPtr<IUnknown> unk;
    Check(sp->QueryService(SID_STopLevelBrowser, &unk));

    // get its service provider
    CComPtr<IServiceProvider> sp2;
    Check(unk->QueryInterface(&sp2));

    // get the tree control
    CComPtr<INameSpaceTreeControl> ctl;
    Check(sp2->QueryService(IID_INameSpaceTreeControl, &ctl));

    // get all roots, "Application Links" is a root
    CComPtr<IShellItemArray> roots;
    Check(ctl->GetRootItems(&roots));

    DWORD count;
    Check(roots->GetCount(&count));

    // search for "Application Links" folder
    for (DWORD i = 0; i < count; i++)
    {
      CComPtr<IShellItem> root;
      Check(roots->GetItemAt(i, &root));

      // get the normalized name, not the display (localized) name
      CComHeapPtr<wchar_t> name;
      Check(root->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &name));

      // CLSID_AppSuggestedLocations?
      if (!lstrcmpi(name, L"::{C57A6066-66A3-4D91-9EB9-41532179F0A5}"))
      {
        // found, expand it
        ctl->SetItemState(root, NSTCIS_EXPANDED, NSTCIS_EXPANDED);

        // get the first child
        // TODO: loop over all suggested location (places) and use the one we're after instead of blindly taking the first one...
        CComPtr<IShellItem> child;
        ctl->GetNextItem(root, NSTCGNI_CHILD, &child);

        if (child.p) // this will probably not succeed the first time we're called
        {
          // select the item
          CComHeapPtr<wchar_t> childName;
          ctl->SetItemState(child, NSTCIS_SELECTED, NSTCIS_SELECTED);
        }
        else
        {
          // select something so we can get back here
      HRCHECK (pfd->SetFolder(location));
        }
        break;
      }
    }
    return S_OK;
  }

  // poor-man's COM implementation for demo purposes...
  HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject)
  {
    *ppvObject = NULL;
    if (riid == IID_IFileDialogEvents)
    {
      *ppvObject = (IFileDialogEvents*)this;
      return S_OK;
    }
    if (riid == IID_IUnknown)
    {
      *ppvObject = (IUnknown*)this;
      return S_OK;
    }
    return E_NOINTERFACE;
  }

  ULONG STDMETHODCALLTYPE AddRef() { return 1; }
  ULONG STDMETHODCALLTYPE Release() { return 1; }
  HRESULT STDMETHODCALLTYPE OnFileOk(IFileDialog* pfd) { return S_OK; }
  HRESULT STDMETHODCALLTYPE OnFolderChanging(IFileDialog* pfd, IShellItem* psiFolder) { return S_OK; }
  HRESULT STDMETHODCALLTYPE OnFolderChange(IFileDialog* pfd) { return S_OK; }
  HRESULT STDMETHODCALLTYPE OnShareViolation(IFileDialog* pfd, IShellItem* psi, FDE_SHAREVIOLATION_RESPONSE* pResponse) { return S_OK; }
  HRESULT STDMETHODCALLTYPE OnTypeChange(IFileDialog* pfd) { return S_OK; }
  HRESULT STDMETHODCALLTYPE OnOverwrite(IFileDialog* pfd, IShellItem* psi, FDE_OVERWRITE_RESPONSE* pResponse) { return S_OK; }
};

注意:为了简单起见,我使用 Visual Studio 的 ATL 智能指针 类。