【问题标题】:How to obtain the PIDL of an IShellFolder如何获取 IShellFolder 的 PIDL
【发布时间】:2009-11-19 20:26:41
【问题描述】:

如果我有一个IShellFolder 接口指针。我怎样才能获得它的 PIDL?

我可以看到如何枚举它的孩子,我可以看到如何使用它来比较任何两个孩子。但是我怎样才能得到它自己的 pidl?

我问是因为我想知道:

这是 IShellFolder == 另一个 IShellFolder

我可以使用IShellFolder::CompareIDs(),但我必须拥有两个文件夹的 ID。

【问题讨论】:

    标签: c++ winapi com windows-shell


    【解决方案1】:

    Chris 或 Mordechai 在 #1 上所写的内容无论如何都不是重点。问题不在于 shell 命名空间中的对象,而在于具有 IShellFolder 接口的对象。拥有IShellFolder 接口本身并不意味着在 shell 命名空间中的存在。最初的问题是不正确的,因为它假设具有IShellFolder 接口的对象必须有“它自己的PIDL”

    我认为,你能做的最好的事情就是 Mordechai 建议的:

    • 查看对象是否也有IPersistFolder2接口

    此接口的目的是在 shell 命名空间中修复对象,这反过来又使文件夹可持久化。与其从没有发布的文档中推断,不如看看微软对IPersistFolderIPersistFolder2 接口以及InitializeGetCurFolder 方法的实际说法。最值得注意的是:

    您需要实现此接口,以便可以检索 Shell 文件夹对象的 ITEMIDLIST。

    关于#2,恐怕克里斯肯定是不正确的。 IShellFolder 当然可以在没有 PIDL 的情况下获得。 Chris 为#1 引入的控制面板为#2 提供了一个现成的反例。只需将CLSID_ControlPanelIID_IShellFolder 提供给CoCreateInstance。您无需“了解 PIDL” 即可获得完美可用的控制面板实例。

    在 SHELL32 中实现了一些其他可创建的 shell 文件夹,任何 DLL 都可以设置任意数量的其他文件夹。

    【讨论】:

      【解决方案2】:

      我发现您可以在 IShellFolder 中查询其 IPersistFolder2,它具有 GetCurFolder(),它返回其绝对 PIDL。然后我可以简单地使用桌面的 IShellFolder 来比较 IDs() 来确定它们是否相等。我在查看SHGetIDListFromObject 时发现了它的轮廓。我不能只使用那个功能,因为它是 Vista,而且我需要兼容 XP。

      这是它如何工作的草图(假设您有一个 ifolder_desktop 和 ifolder_other,它们是 IShellFolder 指针。Pidl 是一个简单的助手,可确保 IDLIST 被正确释放):

      CComQIPtr<IPersistFolder2> ipf2_desktop(ifolder_desktop);
      CComQIPtr<IPersistFolder2> ipf2_folder(ifolder_other);
      
      Pidl pidl_desktop, pidl_folder;
      VERIFY(SUCCEEDED(ipf2_desktop->GetCurFolder(pidl_desktop)));
      VERIFY(SUCCEEDED(ipf2_folder->GetCurFolder(pidl_folder)));
      
      HRESULT hr = ifolder_desktop->CompareIDs(NULL, pidl_desktop, pidl_folder);
      pCmdUI->Enable(SUCCEEDED(hr) && HRESULT_CODE(hr) != 0);
      

      如果有人对我的简单 Pidl 类感兴趣:

      class Pidl
      {
      public:
          // create empty
          Pidl() : m_pidl(NULL) { }
      
          // create one of specified size
          explicit Pidl(size_t size) : m_pidl(Pidl_Create(size)) {}
      
          // create a copy of a given PIDL
          explicit Pidl(const ITEMIDLIST * pidl) : m_pidl(Pidl_Copy(pidl)) {}
      
          // create an absolute PIDL from a parent + child
          Pidl(const ITEMIDLIST_ABSOLUTE * pParent, const ITEMIDLIST_RELATIVE * pChild) : m_pidl(Pidl_Concatenate(pParent, pChild)) { }
      
          // return our PIDL for general use (but retain ownership of it)
          operator const ITEMIDLIST * () { return m_pidl; }
      
          // return a pointer to our pointer, for use in functions that assign to a PIDL
          operator ITEMIDLIST ** () 
          {
              free();
              return &m_pidl; 
          }
      
          // release ownership of our PIDL
          ITEMIDLIST * release() 
          { 
              ITEMIDLIST * pidl = m_pidl;
              m_pidl = NULL;
              return pidl;
          }
      
          void free()
          {
              if (m_pidl)
                  //Pidl_Free(m_pidl);
                  ILFree(m_pidl);
          }
      
          // automatically free our pidl (if we have one)
          ~Pidl()
          {
              free();
          }
      
      private:
          ITEMIDLIST * m_pidl;
      };
      

      【讨论】:

        【解决方案3】:

        我忘了提到SHGetIDListFromObject 函数。

        它仅在 Windows Vista 及更高版本中可用。它的优点是可以记录下来,尽管很简洁。当然,您可以从my own documentation 获得更多详细信息。这表明 Microsoft 知道另外两种获取 PIDL 的方法,以获取指向 shell 命名空间中对象的任意接口指针。

        【讨论】:

        • 感谢您的链接和详细信息。我不记得我是否偶然发现了该功能,但我仅限于维护 XP 兼容性(尽管听起来我可以使用指向 shell32.dll 的动态链接)。
        【解决方案4】:

        Mordachai 的回答可能是正确的,但对我来说,这个查询在两个方面没有意义:

        1. 我不相信有已发布的文档说 IShellFolder 只能有一个父级。任何特定的 shell 文件夹可能有多种方法。可以通过“我的电脑”、“开始”菜单以及您创建的文件系统中的任何位置访问控制面板 连接点到它。似乎 shell 团队最初的意图是,给定一个 IShellFolder 实例,对于外部用户来说,它的任意位置恰好是什么 应该无关紧要。

        2. 另外,任何实例化 IShellFolder 的应用程序肯定都是在了解 PIDL 的情况下这样做的。如果您的应用关心 IShellFolder 的路径,它已经拥有该信息。你是怎么松开的? (以及为什么 shell 团队要添加一种方法来帮助应用跟踪自己的数据?)

        【讨论】:

        • 1.是的,但文件系统是 shell 空间中大多数对象的基本事实。尽管给定文件夹可能有多个链接、连接点等,但实际父文件夹只能有一个。根据文件系统的定义,它是真的。一直都是这样,有 SHBindToParent 来佐证这个事实。 2. 不是这样。可以直接从 SHGetDesktopFolder 实例化 IShellFolder。无论如何,通常设计的系统只能以一种方式最终失败。从一种转换到另一种的能力更加灵活和强大。
        • 1.那么如果某些文件系统有限制怎么办。shell 命名空间旨在表达更一般的情况。 2. 感谢您指出 1 个特殊情况。非常聪明。
        【解决方案5】:

        如前所述,控制面板等特殊文件夹可能存在很多问题(我仍然不完全理解),但这里有一个“普通”文件夹的简单解决方案:

        HRESULT get_pidl(IShellFolder * sf, LPITEMIDLIST * pidl)
        {
            if (!sf || !pidl) return E_FAIL;
        
            wchar_t FolderName[MAX_PATH] = {0};
            STRRET strDispName; 
        
            sf->GetDisplayNameOf(NULL, SHGDN_FORPARSING, &strDispName); 
            StrRetToBuf(&strDispName, NULL, FolderName, (UINT)MAX_PATH);
        
            IShellFolder * desktop = nullptr;
            SHGetDesktopFolder(&desktop);
        
            ULONG cbEaten, atrib = 0;
            HRESULT hr = desktop->ParseDisplayName(NULL, nullptr, FolderName, &cbEaten, pidl, &atrib);
            desktop->Release();
        
            return hr;
        }
        

        【讨论】:

          猜你喜欢
          • 2012-07-11
          • 2012-07-11
          • 2011-01-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-12-26
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多