【问题标题】:How can I add a virtual folder(IShellFolder) to the Explorer view from within a namespace extension?如何从命名空间扩展中将虚拟文件夹 (IShellFolder) 添加到资源管理器视图?
【发布时间】:2017-04-05 06:29:46
【问题描述】:

我有一个正在开发的命名空间扩展,但我无法从扩展中以编程方式添加一个文件夹。视觉上发生的是单击工具栏按钮,选择文件路径并且该文件是创建虚拟文件夹和文件的数据源。

由于工具栏上的加载文件按钮位于命名空间扩展根目录中,我想要发生的事情是让新的虚拟文件夹自动出现。为了显示它,我需要单击树视图或从根目录中向上并返回。然后该文件夹就存在了。

我已经研究过这个问题,通常当发生这种情况时,人们会使用 SHChangeNotify。我尝试了类似下面的示例并使用各种组合,例如提供命名空间扩展根目录的路径或 pidl,下面的示例包括应该存在的新文件夹,使用该路径的 pidl(在 SHChangeNotify 中具有正确的标志)和仍然没有骰子。我还尝试了 SHCNE_xxx ,其中 xxx 是通知所有标志。 SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_IDLIST, retPidl, 0);

右键单击视图窗格并选择刷新不会调用要显示的文件夹。

在获取文件夹路径的代码中,我调用了文件夹的 BindToObject,然后调用了 EnumObjects 和 Next,没有命中断点。当我单击树视图或上一个文件夹并返回时,所有断点都会被命中。 SHChangeNotify 不调用断点。

视图是使用 CreateViewObject 中的 SHCreateShellFolderView 创建的,如下所示:

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();
    }
}

在 ShellVolderView 类中,我在通知标志上设置了一个 bp,但它们从未命中。我读到 SFVM_UPDATEOBJECT 需要返回 S_OK,所以我添加了它。

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;
}

*编辑:添加可疑函数。当 *ppidl 为 NULL 时,返回 E_INVALIDARG。

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;
}

我应该采取哪些步骤以使文件夹以编程方式显示?

【问题讨论】:

  • SHCNE_UDPATEDIR 说“嘿,如果有人在看thenewfoldername,他们应该刷新。”但是没有人在看thenewfoldername,因为它不存在。文档说:“如果文件夹已被创建、删除或重命名,请分别使用 SHCNE_MKDIR、SHCNE_RMDIR 或 SHCNE_RENAMEFOLDER。”
  • 命名空间扩展数据源是一个独立于 SHITEMIDLIST 的树结构。在更新数据源树以从根中添加另一个分支并正确调用 SHChangeNotify(谢谢)之后,我应该采取哪些其他步骤来获取/要求 Shell 调用 IShellFolder2::EnumObjects()?
  • 为每个创建的目录增加一个 SHCNE_MKDIR。如果你创建了一个子子目录,那就意味着提升它两次,一级目录一次,二级目录一次
  • 我确实需要使用显式的 SCHNE_MKDIR 和 SCHNE_RMDIR,SCHNE_UPDATE 并没有像很多人使用的那样工作。添加和删​​除目录现在与其他一些修复一起工作。顺便说一句,我从 97 年到 11 年在 Windows 中工作。我很高兴得到你的帮助。

标签: winapi com windows-shell


【解决方案1】:

尝试以下方法:

SFVM_GETNOTIFY:
  begin
    PDWORD(ALParam)^ := SHCNE_ALLEVENTS;
    Result := S_OK;
  end;

【讨论】:

  • 在 SFVM_GETNOTIFY 中,我现在按照您的建议设置标志并在 lParam 中设置 pidl,但在发出 SHChangeNotify 后会调用 IShellFolder:EnumObjects 或 BindToObject。但是 SFVM_FSNOTIFY 现在受到了打击。用户界面没有变化,文件夹不显示不使用树或向上并返回根文件夹。
  • 如果您使用 DefShellView,您可以尝试删除您的 SFVM_UPDATEOBJECT、SFVM_QUERYFSNOTIFY、SFVM_ADDOBJECT 和 SFVM_FSNOTIFY 处理程序。如果这个值只返回 E_NOTIMPL,因为这个值应该由 DefShellView 处理。
  • 这是有道理的。我盲目地说在这些情况下不做任何事情都可以。感谢那。我越来越近了,但是在通过 SHChangeNotify 发送事件通知后仍然没有调用 EnumObject。我认为一旦调用 EnumObjects,Explorer 将通过调用 Enumerator 接口显示新数据,然后我可以创建 SHITEMID。有什么想法吗?
  • 我赞成你的问题,但为什么你需要 EnumObjects?
  • Shell 不知道我的数据源中的新文件夹。我认为 Shell 需要调用 EnumObjects 来获取我的枚举器(IEnumIDList)。然后,Shell 将调用调用 IEnumIDList::Next(),在其中我从我的数据源中为新文件夹创建 SHITEMID 并返回它们。至少我认为它应该是这样工作的,我以前从未创建过命名空间扩展。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-11-10
  • 1970-01-01
  • 2011-11-20
  • 1970-01-01
  • 1970-01-01
  • 2011-06-19
  • 2015-03-02
相关资源
最近更新 更多