实现基本文件夹对象接口

实现命名空间扩展的过程类似于任何其他进程内组件对象模型 (COM) 对象的过程。 所有扩展都必须支持三个主要接口,这些接口为Windows资源管理器提供在树视图中显示扩展文件夹所需的基本信息。 但是,若要充分利用Windows资源管理器的功能,扩展还必须公开一个或多个可选接口,这些接口支持更复杂的功能,例如快捷菜单或拖放,并提供文件夹视图。

本文档讨论如何实现Windows资源管理器调用有关扩展内容的信息的主接口和可选接口。 有关如何实现文件夹视图以及如何自定义Windows资源管理器的讨论,请参阅“实现文件夹视图”。

基本实现和注册

作为进程内 COM 服务器,DLL 必须公开多个标准函数和接口:

这些函数和接口的实现方式与大多数其他 COM 对象的方式相同。 有关详细信息,请参阅 COM 文档

注册扩展

与所有 COM 对象一样,必须为扩展创建 (CLSID) GUID 的类标识符。 通过创建为扩展的 CLSID 命名的 HKEY_CLASSES_ROOT\CLSID 子项来注册对象。 DLL 应注册为进程内服务器,并应指定单元线程模型。 可以通过向扩展的 CLSID 键添加各种子项和值来自定义扩展根文件夹的行为。

其中几个值仅适用于具有虚拟交界点的扩展。 这些值不适用于其交界点为文件系统文件夹的扩展。 有关进一步讨论,请参阅 指定命名空间扩展的位置。 若要使用虚拟交点修改扩展的行为,请将以下一个或多个值添加到扩展的 CLSID 键中:

  • WantsFORPARSING。 具有虚拟交点的扩展分析名称通常采用 ::{GUID} 格式。 此类型的扩展通常包含虚拟项。 但是,某些扩展(如“我的文档”)实际上对应于文件系统文件夹,即使它们具有虚拟交界点。 如果扩展以这种方式表示文件系统对象,则可以设置 WantsFORPARSING 值。 然后,Windows资源管理器将通过调用文件夹对象的 IShellFolder::GetDisplayNameOf 方法来请求根文件夹分析名称,并将 uFlags 设置为 SHGDN_FORPARSING并将 pidl 设置为指向项标识符列表的单个空指针, (PIDL) 。 空 PIDL 仅包含终止符。 然后,方法应返回根文件夹的 ::{GUID} 分析名称。
  • HideFolderVerbs。 在 HKEY_CLASSES_ROOT\Folder 下注册的谓词通常与所有扩展相关联。 它们显示在扩展的快捷菜单上,可由 ShellExecute 调用。 若要防止这些谓词中的任何一个与扩展相关联,请设置 HideFolderVerbs 值。
  • HideAsDelete。 如果用户尝试删除扩展,Windows资源管理器将隐藏扩展。
  • HideAsDeletePerUser。 此值的效果与 HideAsDelete 相同,但基于每个用户。 该扩展仅隐藏那些尝试删除该扩展的用户。 该扩展对所有其他用户可见。
  • QueryForOverlay。 设置此值以指示根文件夹的图标可以有图标覆盖。 文件夹对象必须支持 IShellIconOverlay 接口。 在Windows资源管理器显示根文件夹的图标之前,它将通过调用两个 IShellIconOverlay 方法之一(将 pidlItem 设置为空 PIDL)来请求覆盖图标。

其余值和子项适用于所有扩展:

  • 若要指定扩展的交界点文件夹的显示名称,请将扩展的 CLSID 子项的默认值设置为适当的字符串。
  • 当光标悬停在文件夹上时,通常会显示描述文件夹内容的信息提示。 若要为扩展的根文件夹提供信息提示,请为扩展的 CLSID 键创建 InfoTip REG_SZ 值,并将其设置为适当的字符串。
  • 若要为扩展的根文件夹指定自定义图标,请创建名为 DefaultIcon 的扩展 CLSID 子项的子项。 将 DefaultIcon 的默认值设置为一个 REG_SZ 值,该值包含包含图标的文件的名称,后跟逗号,后跟减号,后跟该文件中图标的索引。
  • 默认情况下,扩展根文件夹的快捷菜单将包含 HKEY_CLASSES_ROOT\Folder下定义的项。 如果已设置相应的SFGAO_XXX标志,则添加 “删除”、“ 重命名”和 “属性” 项。 可以将其他项目添加到根文件夹的快捷菜单,或替代现有项,就像对 文件类型一样。 在扩展的 CLSID 键下创建 Shell 子项,并按照 扩展快捷菜单中所述定义命令。
  • 如果需要更灵活的方法来处理根文件夹的快捷菜单,则可以实现 快捷菜单处理程序。 若要注册快捷菜单处理程序,请在扩展的 CLSID 键下创建 ShellEx 键。 像常规 创建 Shell 扩展处理程序一样注册处理程序的 CLSID。
  • 若要将页面添加到根文件夹的属性属性表,请为文件夹提供 SFGAO_HASPROPSHEET 属性并实现 属性表处理程序。 若要注册属性表处理程序,请在扩展的 CLSID 密钥下创建 ShellEx 密钥。 像常规 创建 Shell 扩展处理程序一样注册处理程序的 CLSID。
  • 若要指定根文件夹的属性,请将 ShellFolder 子项添加到扩展的 CLSID 子项。 创建 Attributes 值,并将其设置为 SFGAO_XXX 标志的适当组合。

下表列出了根文件夹的一些常用属性。

标志 描述
SFGAO_FOLDER 0x20000000 扩展的根文件夹包含一个或多个项目。
SFGAO_HASSUBFOLDER 0x80000000 扩展的根文件夹包含一个或多个子文件夹。 Windows资源管理器将放置文件夹图标旁边的加号 ( + ) 。
SFGAO_CANDELETE 0x00000020 该扩展的根文件夹可由用户删除。 文件夹的快捷菜单将包含 “删除” 项。 应为放置在其中一个 虚拟文件夹下的交界点设置此标志。
SFGAO_CANRENAME 0x00000010 扩展的根文件夹可由用户重命名。 文件夹的快捷菜单将具有 “重命名 ”项。
SFGAO_HASPROPSHEET 0x00000040 扩展的根文件夹具有 Properties 属性表。 文件夹的快捷菜单将具有 “属性” 项。 若要提供属性表,必须实现 属性表处理程序。 如前所述,在扩展的 CLSID 密钥下注册处理程序。

 

以下示例显示了具有 MyExtension 显示名称的扩展的 CLSID 注册表项。 该扩展具有一个自定义图标,该图标包含在扩展的 DLL 中,索引为 1。 设置 SFGAO_FOLDERSFGAO_HASSUBFOLDERSFGAO_CANDELETE 属性。

HKEY_CLASSES_ROOT
   CLSID
      {Extension CLSID}
         (Default) = MyExtension
         InfoTip = Some appropriate text
      DefaultIcon
         (Default) = c:\MyDir\MyExtension.dll,-1
      InProcServer32
         (Default) = c:\MyDir\MyExtension.dll
         ThreadingModel = Apartment
      ShellFolder
         Attributes = 0xA00000020

处理 PIDL

Shell 命名空间中的每个项都必须具有唯一的 PIDL。 Windows资源管理器将 PIDL 分配给根文件夹,并在初始化期间将值传递给扩展。 然后,扩展负责将正确构造的 PIDL 分配给其每个对象,并按请求将这些 PIDL 提供给Windows资源管理器。 当 Shell 使用 PIDL 来标识某个扩展的对象时,扩展必须能够解释 PIDL 并标识特定对象。 扩展还必须为每个对象分配显示名称和分析名称。 由于几乎每个文件夹接口都使用 PIDL,因此扩展通常实现单个 PIDL 管理器 来处理所有这些任务。

PIDL 对于 ITEMIDLIST 结构或指向此类结构的指针(具体取决于上下文)而言是短的。 如声明的那样, ITEMIDLIST 结构具有单个成员 ,即 SHITEMID 结构。 对象的 ITEMIDLIST 结构实际上是两个或多个 SHITEMID 结构的打包数组。 这些结构的顺序定义命名空间的路径,这与 c:\MyDirectory\MyFile 定义通过文件系统的路径的方式大致相同。 通常,对象的 PIDL 将包含一系列与定义命名空间路径的文件夹对应的 SHITEMID 结构,后跟对象的 SHITEMID 结构,后跟终止符。

终止符是 SHITEMID 结构, cb 成员设置为 NULL。 终止符是必需的,因为对象的 PIDL 中的 SHITEMID 结构数取决于 Shell 命名空间中对象的位置以及路径的起点。 此外,各种 SHITEMID 结构的大小可能会有所不同。 收到 PIDL 时,无法简单地确定其大小,甚至确定 SHITEMID 结构的总数。 相反,必须按结构“引导”打包的数组,直到到达终止符。

若要创建 PIDL,应用程序需要:

  1. 为每个对象创建 SHITEMID 结构。
  2. 将相关的 SHITEMID 结构组合到 PIDL 中。

创建 SHITEMID 结构

对象的 SHITEMID 结构唯一标识其文件夹中的对象。 事实上,许多 IShellFolder 方法使用的 PIDL 类型只包含对象的 SHITEMID 结构,后跟终止符。 SHITEMID 结构的定义是:

typedef struct _SHITEMID { 
    USHORT cb; 
    BYTE   abID[1]; 
} SHITEMID, * LPSHITEMID;

abID 成员是对象的标识符。 由于 abID 的长度未定义且可能有所不同, 因此 cb 成员设置为 SHITEMID 结构的大小(以字节为单位)。

由于 abID 的长度和内容均未标准化,因此可以使用要向对象分配 abID 值的任何方案。 唯一的要求是不能在同一文件夹中具有相同值的两个对象。 但是,出于性能原因, SHITEMID 结构应与 DWORD 对齐。 换句话说,应构造 abID 值,以便 cb 是 4 的整数倍数。

通常, abID 指向扩展定义的结构。 除了对象的 ID 之外,此结构还通常用于保存各种相关信息,例如对象的类型或属性。 然后,扩展的文件夹对象可以快速从 PIDL 中提取信息,而无需查询它。

注意

设计 PIDL 数据结构的最重要方面之一是使结构持久化且可传输。 在 PIDL 的上下文中,这些术语的含义是:

  • 可持久保存。 系统经常将 PIDL 置于各种类型的长期存储中,例如快捷方式文件。 然后,它可以在系统重新启动后从存储中恢复这些 PIDL。 从存储中恢复的 PIDL 仍对扩展有效且有意义。 例如,此要求意味着不应在 PIDL 结构中使用指针或句柄。 当系统稍后从存储中恢复这些数据时,包含此类数据的 PIDL 通常毫无意义。
  • 运输。 从一台计算机传输到另一台计算机时,PIDL 必须保持有意义。 例如,可以将 PIDL 写入快捷方式文件,复制到软盘,并传输到另一台计算机。 该 PIDL 对于第二台计算机上运行的扩展仍有意义。 例如,若要确保 PIDL 可传输,请显式使用 ANSI 或 Unicode 字符。 避免使用 TCHARLPTSTR 等数据类型。 如果使用这些数据类型,在运行 Unicode 版本的扩展的计算机上创建的 PIDL 将无法由另一台计算机上运行的该扩展的 ANSI 版本读取。

 

以下声明显示了数据结构的简单示例。

typedef struct tagMYPIDLDATA {
  USHORT cb;
  DWORD dwType;
  WCHAR wszDisplayName[40];
} MYPIDLDATA, *LPMYPIDLDATA;

cb 成员设置为 MYPIDLDATA 结构的大小。 此成员使 MYPIDLDATA 成为有效的 SHITEMID 结构,并本身。 其余成员等效于 SHITEMID 结构的 abID 成员并保存私有数据。 dwType 成员是一个扩展定义的值,该值指示对象的类型。 对于此示例, dwType 对于文件夹设置为 TRUE ,否则为 FALSE 。 例如,此成员允许快速确定对象是否为文件夹。 wszDisplayName 成员包含对象的显示名称。 由于不会将相同的显示名称分配给同一文件夹中的两个不同的对象,因此显示名称也用作对象 ID。 在此示例中, wszDisplayName 设置为 40 个字符,以确保 SHITEMID 结构将与 DWORD 对齐。 若要限制 PIDL 的大小,可以改用可变长度字符数组并相应地调整 cb 的值。 用足够的“\0”字符填充显示字符串,以保持结构的 DWORD 对齐方式。 在结构中可能有用的其他成员包括对象的大小、属性或分析名称。

构造 PIDL

为对象定义 SHITEMID 结构后,可以使用它们来构造 PIDL。 可以出于各种目的构造 PIDL,但大多数任务使用两种类型的 PIDL 之一。 最简单的单级 PIDL 标识相对于其父文件夹的对象。 许多 IShellFolder 方法都使用这种类型的 PIDL。 单级 PIDL 包含对象的 SHITEMID 结构,后跟终止符。 完全限定的 PIDL 通过从桌面到对象的命名空间层次结构定义路径。 这种类型的 PIDL 从桌面开始,并包含路径中每个文件夹的 一个 SHITEMID 结构,后跟对象和终止符。 完全限定的 PIDL 唯一标识整个 Shell 命名空间中的对象。

构造 PIDL 的最简单方法是直接使用 ITEMIDLIST 结构本身。 创建 ITEMIDLIST 结构,但分配足够的内存来容纳所有 SHITEMID 结构。 此结构的地址将指向初始 SHITEMID 结构。 定义此初始结构的成员的值,然后按适当的顺序追加尽可能多的附加 SHITEMID 结构。 以下过程概述了如何创建单级 PIDL。 它包含两个 SHITEMID 结构- MYPIDLDATA 结构,后跟终止符:

  1. 使用 CoTaskMemAlloc 函数为 PIDL 分配内存。 为专用数据分配足够的内存,以及终止符的 USHORT (两个字节) 。 将结果强制转换为 LPMYPIDLDATA。
  2. 将第一个 MYPIDLDATA 结构的 cb 成员设置为该结构的大小。 在本示例中,将 cb 设置为 sizeof (MYPIDLDATA) 。 如果要使用可变长度结构,则必须计算 cb 的值。
  3. 为专用数据成员分配适当的值。
  4. 计算下一 个 SHITEMID 结构的地址。 将当前 MYPIDLDATA 结构的地址强制转换为 LPBYTE,并将该值添加到步骤 3 中确定的 cb 值。
  5. 在这种情况下,下一个 SHITEMID 结构是终止符。 将结构的 cb 成员设置为零。

对于更长的 PIDL,请分配足够的内存,并为每个额外的 SHITEMID 结构重复步骤 3-5。

下面的示例函数采用对象的类型和显示名称,并返回对象的单级 PIDL。 该函数假定显示名称(包括其终止 null 字符)不会超过为 MYPIDLDATA 结构声明的字符数。 如果该假设被证明是错误的, StringCbCopyW 函数将截断显示名称。 g_pMalloc变量是在其他地方创建的 IMalloc 指针,存储在全局变量中。

LPITEMIDLIST CreatePIDL(DWORD dwType, LPCWSTR pwszDisplayName)
{
    LPMYPIDLDATA   pidlOut;
    USHORT         uSize;

    pidlOut = NULL;

    //Calculate the size of the MYPIDLDATA structure.
    uSize = sizeof(MYPIDLDATA);

    // Allocate enough memory for the PIDL to hold a MYPIDLDATA structure 
    // plus the terminator
    pidlOut = (LPMYPIDLDATA)m_pMalloc->Alloc(uSize + sizeof(USHORT));

    if(pidlOut)
    {
       //Assign values to the members of the MYPIDLDATA structure
       //that is the PIDL's first SHITEMID structure
       pidlOut->cb = uSize;
       pidlOut->dwType = dwType;
       hr = StringCbCopyW(pidlOut->wszDisplayName, 
                          sizeof(pidlOut->wszDisplayName), pwszDisplayName);
       
       // TODO: Add error handling here to verify the HRESULT returned 
       // by StringCbCopyW.

       //Advance the pointer to the start of the next SHITEMID structure.
       pidlOut = (LPMYPIDLDATA)((LPBYTE)pidlOut + pidlOut->cb);

       //Create the terminating null character by setting cb to 0.
       pidlOut->cb = 0;
    }

    return pidlOut;

完全限定的 PIDL 必须具有从桌面到对象的每个对象的 SHITEMID 结构。 当 Shell 调用 IPersistFolder::Initialize 时,扩展会收到根文件夹的完全限定 PIDL。 若要为对象构造完全限定的 PIDL,请获取 Shell 分配给根文件夹的 PIDL,并将从根文件夹带到对象所需的 SHITEMID 结构。

解释 PIDL

当 Shell 或应用程序调用某个扩展的接口来请求有关对象的信息时,它通常会通过 PIDL 标识该对象。 某些方法(如 IShellFolder::GetUIObjectOf)使用相对于父文件夹的 PIDL,并且易于解释。 但是,扩展可能还会收到完全限定的 PIDL。 然后,PIDL 管理器必须确定 PIDL 引用的对象。

将对象与完全限定的 PIDL 关联的任务复杂化在于,PIDL 中的一个或多个初始 SHITEMID 结构可能属于位于 Shell 命名空间中扩展外部的对象。 你无法解释这些结构的 abID 成员的含义。 扩展必须执行的操作是“演练” SHITEMID 结构列表,直到到达与根文件夹对应的结构。 此后,你将了解如何解释 SHITEMID 结构中的信息。

若要执行 PIDL,请获取第一 个 cb 值并将其添加到 PIDL 的地址,以将指针前进到下一 个 SHITEMID 结构的开头。 然后,它将指向该结构的 cb 成员,你可以使用该成员将指针前进到下一 个 SHITEMID 结构的开头等。 每次推进指针时,检查 SHITEMID 结构以确定是否已到达扩展命名空间的根目录。

实现主接口

与所有 COM 对象一样,实现扩展在很大程度上是实现接口集合的问题。 本部分讨论必须由所有扩展实现的三个主要接口。 它们用于初始化,并提供有关扩展内容的基本信息Windows资源管理器。 这些接口以及 文件夹视图是功能扩展所需的所有接口。 但是,为了充分利用Windows资源管理器的功能,大多数扩展还实现一个或多个可选接口。

IPersistFolder 接口

调用 IPersistFolder 接口来初始化新的文件夹对象。 IPersistFolder::Initialize 方法将完全限定的 PIDL 分配给新对象。 Microsoft Store此 PIDL 供以后使用。 例如,文件夹对象必须使用此 PIDL 为对象的子级构造完全限定的 PIDL。 文件夹对象的创建者还可以调用 IPersist::GetClassID 来请求对象的 CLSID。

通常,文件夹对象由其父文件夹的 IShellFolder::BindToObject 方法创建和初始化。 但是,当用户浏览到扩展时,Windows资源管理器创建并初始化扩展的根文件夹对象。 根文件夹对象通过 IPersistFolder::Initialize 接收的 PIDL 包含从桌面到根文件夹的路径,你需要为扩展构造完全限定的 PIDL。

IShellFolder 接口

Shell 将扩展视为文件夹对象的分层排序集合。 IShellFolder 接口是任何扩展实现的核心。 它表示文件夹对象,并提供Windows资源管理器,其中包含显示文件夹内容所需的大部分信息。

IShellFolder 通常是由文件夹对象直接公开的 IPersistFolder 以外的唯一文件夹接口。 虽然Windows资源管理器使用各种必需和可选接口来获取有关文件夹内容的信息,但它通过 IShellFolder 获取指向这些接口的指针。

Windows资源管理器通过多种方式获取扩展根文件夹的 CLSID。 有关详细信息,请参阅 指定命名空间扩展的位置显示命名空间扩展的Self-Contained视图。 Windows资源管理器随后使用该 CLSID 创建和初始化根文件夹的实例,并查询 IShellFolder 接口。 扩展创建一个文件夹对象来表示根文件夹并返回对象的 IShellFolder 接口。 扩展和 Windows Explorer 之间剩余的大部分交互都通过 IShellFolder 进行。 Windows资源管理器调用 IShellFolder 以:

  • 请求可以枚举根文件夹的内容的对象。
  • 获取有关根文件夹内容的各种类型的信息。
  • 请求公开其中一个可选接口的对象。 然后,可以查询这些接口以获取其他信息,例如图标或快捷菜单。
  • 请求表示根文件夹的子文件夹的文件夹对象。

当用户打开根文件夹的子文件夹时,Windows资源管理器调用 IShellFolder::BindToObject。 扩展创建并初始化一个新文件夹对象来表示子文件夹并返回其 IShellFolder 接口。 然后,Windows资源管理器调用此接口以获取各种类型的信息,依此类说,直到用户决定在 Shell 命名空间中的其他位置导航或关闭Windows资源管理器。

本部分的其余部分简要讨论了更重要的 IShellFolder 方法以及如何实现它们。

EnumObjects

在树视图中显示文件夹的内容之前,Windows资源管理器必须先通过调用 IShellFolder::EnumObjects 方法来确定文件夹包含的内容。 此方法创建一个标准 OLE 枚举对象,该对象公开 IEnumIDList 接口并返回该接口指针。 IEnumIDList 接口允许Windows资源管理器获取文件夹包含的所有对象的 PIDL。 然后,这些 PIDL 用于获取有关文件夹包含的对象的信息。 有关更多详细信息,请参阅 IEnumIDList 接口

注意

IEnumIDList::Next 方法应仅返回相对于父文件夹的 PIDL。 PIDL 应仅包含对象的 SHITEMID 结构,后跟终止符。

 

CreateViewObject

在显示文件夹的内容之前,Windows资源管理器调用此方法以请求指向 IShellView 接口的指针。 Windows资源管理器使用此接口来管理文件夹视图。 创建文件夹视图对象并返回其 IShellView 接口。

还调用 IShellFolder::CreateViewObject 方法,请求文件夹本身的可选接口之一,例如 IContextMenu。 此方法的实现应创建一个对象,该对象公开请求的接口并返回接口指针。 如果Windows资源管理器需要文件夹包含的某个对象的可选接口,它将调用 IShellFolder::GetUIObjectOf

GetUIObjectOf

虽然可通过 IShellFolder 方法获取有关文件夹内容的基本信息,但扩展还可以为 Windows Explorer 提供各种附加信息。 例如,可以为文件夹或对象的快捷菜单的内容指定图标。 Windows资源管理器调用 IShellFolder::GetUIObjectOf 方法以尝试检索有关文件夹包含的对象的其他信息。 Windows资源管理器指定要获取的信息的对象,以及相关接口的 IID。 然后,文件夹对象创建一个对象,该对象公开请求的接口并返回接口指针。

如果扩展允许用户使用拖放或剪贴板传输对象,Windows资源管理器将调用 IShellFolder::GetUIObjectOf 来请求 IDataObjectIDropTarget 接口。 有关详细信息,请参阅 使用拖放和剪贴板传输 Shell 对象

Windows资源管理器在需要与文件夹本身相同的信息时调用 IShellFolder::CreateViewObject

BindToObject

Windows资源管理器在用户尝试打开某个扩展的子文件夹时调用 IShellFolder::BindToObject 方法。 如果 riid 设置为IID_IShellFolder,则应创建并初始化表示子文件夹的文件夹对象并返回对象的 IShellFolder 接口。

注意

目前,Windows资源管理器仅调用此方法以请求 IShellFolder 接口。 但是,不要假设这种情况始终如此。 在继续操作之前,应始终检查 riid 的值。

 

GetDisplayNameOf

Windows资源管理器调用 IShellFolder::GetDisplayNameOf 方法,将其中一个文件夹对象的 PIDL 转换为名称。 该 PIDL 必须相对于对象的父文件夹。 换句话说,它必须包含单个非NULLSHITEMID 结构。 由于有多个方法可以命名对象,Windows资源管理器通过在 uFlags 参数中设置一个或多个 SHGDNF 标志来指定名称类型。 将设置两个值之一 (SHGDN_NORMALSHGDN_INFOLDER)以指定名称是相对于文件夹还是相对于桌面。 另外三个值之一 (SHGDN_FOREDITINGSHGDN_FORADDRESSBARSHGDN_FORPARSING)可以设置为指定将用于的名称。

必须以 STRRET 结构的形式返回名称。 如果未设置 SHGDN_FOREDITINGSHGDN_FORADDRESSBARSHGDN_FORPARSING ,则返回对象的显示名称。 如果设置了SHGDN_FORPARSING标志,Windows资源管理器请求分析名称。 分析名称将传递给 IShellFolder::P arseDisplayName 以获取对象的 PIDL,即使它可能位于命名空间层次结构中当前文件夹以下的一个或多个级别。 例如,文件系统对象的分析名称是其路径。 可以将文件系统中任何对象的完全限定路径传递给桌面的 IShellFolder::P arseDisplayName 方法,它将返回对象的完全限定 PIDL。

虽然分析名称是文本字符串,但它们不一定必须包含显示名称。 应根据调用 IShellFolder::P arseDisplayName 时最有效的方式分配分析名称。 例如,许多 Shell 的虚拟文件夹不是文件系统的一部分,并且没有完全限定的路径。 而是为每个文件夹分配一个 GUID,分析名称采用 ::{GUID} 格式。 无论使用哪种方案,它都应该能够可靠地“往返”。例如,如果调用方将分析名称传递给 IShellFolder::P arseDisplayName 以检索对象的 PIDL,然后将该 PIDL 传递给具有设置SHGDN_FORPARSING标志的 IShellFolder::GetDisplayNameOf,则调用方应恢复原始分析名称。

GetAttributesOf

Windows资源管理器调用 IShellFolder::GetAttributesOf 方法来确定文件夹对象包含的一个或多个项的属性。 cidl 的值提供查询中的项数,aPidl 指向其 PIDL 的列表。

由于某些属性的测试可能非常耗时,因此Windows资源管理器通常通过在 rfgInOut 中设置其值,将查询限制为可用标志的子集。 方法应仅测试在 rfgInOut 中设置其标志的那些属性。 保留设置的有效标志并清除其余标志。 如果查询中包含多个项,则仅设置应用于所有项的标志。

注意

必须正确设置属性才能正确显示项。 例如,如果项是包含子文件夹的文件夹,则必须设置 SFGAO_HASSUBFOLDER 标志。 否则,Windows资源管理器将不会在树视图中显示项图标旁边的 +。

 

ParseDisplayName

IShellFolder::P arseDisplayName 方法在某种程度上是 IShellFolder::GetDisplayNameOf 的镜像图像。 此方法的最常见用途是将对象的分析名称转换为关联的 PIDL。 分析名称可以引用命名空间层次结构中文件夹下方的任何对象。 返回的 PIDL 相对于公开该方法的文件夹对象,通常不完全限定。 换句话说,尽管 PIDL 可以包含多个 SHITEMID 结构,但第一个结构可以是对象本身或从文件夹到对象的路径中的第一个子文件夹。 调用方必须将此 PIDL 追加到文件夹的完全限定 PIDL,才能获取对象的完全限定 PIDL。

还可以调用 IShellFolder::P arseDisplayName 来请求对象的属性。 由于确定所有适用的属性可能非常耗时,因此调用方将仅设置表示调用方感兴趣的信息的 SFGAO_XXX 标志。 应确定对象中的哪一个属性为 true,并清除剩余的标志。

IEnumIDList 接口

当Windows资源管理器需要枚举文件夹包含的对象时,它将调用 IShellFolder::EnumObjects。 文件夹对象必须创建一个枚举对象,该对象公开 IEnumIDList 接口并返回该接口指针。 Windows资源管理器通常使用 IEnumIDList 枚举文件夹包含的所有对象的 PIDL。

IEnumIDList 是一个标准的 OLE 枚举接口,以通常的方式实现。 但是,请记住,返回的 PIDL 必须相对于文件夹,并且仅包含对象的 SHITEMID 结构和终止符。

实现可选接口

扩展的文件夹对象可以支持许多可选的 Shell 接口。 其中许多内容(如 IExtractIcon)允许自定义用户查看扩展的方式的各个方面。 其他扩展(如 IDataObject)允许扩展支持拖放等功能。

任何可选接口都不由文件夹对象直接公开。 相反,Windows Explorer 调用两种 IShellFolder 方法之一来请求接口:

为了提供信息,文件夹对象会创建一个对象,该对象公开请求的接口并返回接口指针。 Windows资源管理器然后调用该接口来检索所需的信息。 本部分讨论最常用的可选接口。

IExtractIcon

Windows资源管理器在显示文件夹内容之前请求 IExtractIcon 接口。 该接口允许扩展为文件夹包含的对象指定自定义图标。 否则,将使用标准文件和文件夹图标。 若要提供自定义图标,请创建一个图标提取对象,该对象公开 IExtractIcon 并返回指向该接口的指针。 有关进一步讨论,请参阅 IExtractIcon 参考文档或 创建图标处理程序

IContextMenu

当用户右键单击对象时,Windows资源管理器请求 IContextMenu 接口。 若要为对象提供快捷菜单,请创建菜单处理程序对象并返回其 IContextMenu 接口。

创建菜单处理程序对象的过程与用于创建菜单处理程序 Shell 扩展的过程非常相似。 有关详细信息,请参阅创建上下文菜单处理程序IContextMenu、IContextMenu2IContextMenu3 参考。

IQueryInfo

Windows资源管理器调用 IQueryInfo 接口以检索信息提示文本字符串。

IDataObject 和 IDropTarget

当对象由Windows资源管理器显示时,文件夹对象无法直接知道用户何时尝试剪切、复制或拖动对象。 相反,Windows资源管理器请求 IDataObject 接口。 若要允许传输对象,请创建一个数据对象,并返回指向其 IDataObject 接口的指针。

同样,用户可能会尝试在某个对象的Windows资源管理器表示形式(例如图标或地址栏路径)上删除数据对象。 Windows资源管理器,然后请求 IDropTarget 接口。 若要允许删除数据对象,请创建一个对象,该对象公开 IDropTarget 接口并返回接口指针。

处理数据传输是写入命名空间扩展的一个更棘手方面。 有关详细讨论,请参阅 使用拖放和剪贴板传输 Shell 对象

使用默认 Shell 文件夹视图实现

使用默认 Shell 文件夹视图对象的数据源 (DefView) 必须实现以下接口:

(可选)还可以实现 IPersistFolder3