创建 Shell 扩展处理程序

可以使用注册表项和.ini文件扩展 Shell 的功能。 虽然这种扩展 Shell 的方法很简单,并且足以满足许多目的,但这种方法是有限的。 例如,如果使用注册表为某个文件类型指定自定义图标,则对于该类型的每个文件,都将显示相同的图标。 使用注册表扩展 Shell 不允许更改相同类型的不同文件的图标。 Shell 的其他方面(如右键单击文件时可以显示的 “属性”属性 表)根本无法通过注册表进行修改。

扩展 Shell 的一种更强大、更灵活的方法是实现 shell 扩展处理程序。 这些处理程序可以针对 Shell 可以执行的各种操作实现。 在执行操作之前,Shell 会查询扩展处理程序,使其有机会修改操作。 一个常见示例是快捷菜单扩展处理程序。 如果为某个文件类型实现了一个,则每次右键单击其中一个文件时,都会对其进行查询。 然后,处理程序可以逐个文件指定其他菜单项,而不是为整个文件类型设置相同的菜单项。

本文档讨论如何实现允许修改各种 Shell 操作的扩展处理程序。 以下处理程序与特定文件类型相关联,并允许逐个文件指定:

Handler 说明
快捷菜单处理程序 在显示文件的快捷菜单之前调用。 它使你能够逐个文件将项添加到快捷菜单。
数据处理程序 在对 dragShell 对象执行拖放操作时调用。 它使你能够向放置目标提供其他剪贴板格式。
删除处理程序 在文件上拖动或丢弃数据对象时调用。 它使你能够将文件创建到放置目标中。
图标处理程序 在显示文件图标之前调用。 它使你能够逐个将文件的默认图标替换为自定义图标。
属性表处理程序 在显示对象的 Properties 属性表之前调用。 它使你能够添加或替换页面。
缩略图图像处理程序 提供表示项的图像。
Infotip 处理程序 当用户将鼠标指针悬停在 对象上时提供弹出文本。
元数据处理程序 提供对存储在文件中的元数据 (属性) 读取和写入访问权限。 这可用于扩展“详细信息”视图、信息提示、属性页和分组功能。

 

其他处理程序不与特定文件类型关联,但在某些 Shell 操作之前调用:

Handler 说明
栏处理程序 在显示文件夹的“详细信息”视图之前,由 Windows 资源管理器调用。 它使你能够将自定义列添加到“详细信息”视图。
复制挂钩处理程序 当文件夹或打印机对象即将移动、复制、删除或重命名时调用。 它使你能够批准或否决操作。
拖放处理程序 使用鼠标右键拖动文件时调用。 它使你能够修改显示的快捷菜单。
图标覆盖处理程序 在显示文件图标之前调用。 它使你可以为文件的图标指定覆盖层。
搜索处理程序 调用 以启动搜索引擎。 它使你能够实现可从 “开始” 菜单或 Windows 资源管理器访问的自定义搜索引擎。

 

上面列出的部分介绍了如何实现特定扩展处理程序的详细信息。 本文档的其余部分介绍了所有 Shell 扩展处理程序常见的一些实现问题。

实现 Shell 扩展处理程序

Shell 扩展处理程序对象的大部分实现取决于其类型。 但是,存在一些常见元素。 本部分讨论由所有 Shell 扩展处理程序共享的实现的那些方面。

许多 Shell 扩展处理程序是进程内组件对象模型 (COM) 对象。 必须为其分配 GUID 并按照注册 Shell 扩展处理程序中所述进行注册。 它们作为 DLL 实现,必须导出以下标准函数:

  • DllMain。 DLL 的标准入口点。
  • DllGetClassObject。 公开对象的类工厂。
  • DllCanUnloadNow。 COM 调用此函数以确定对象是否为任何客户端提供服务。 否则,系统可以卸载 DLL 并释放关联的内存。

与所有 COM 对象一样,Shell 扩展处理程序必须实现 IUnknown 接口和 类工厂。 大多数扩展处理程序还必须在 Windows XP 或更早版本中实现 IPersistFileIShellExtInit 接口。 这些内容被 Windows Vista 中的 IInitializeWithStreamIInitializeWithItemIInitializeWithFile 所取代。 Shell 使用这些接口来初始化处理程序。

IPersistFile 接口必须通过以下方式实现:

  • 数据处理程序
  • 删除处理程序

过去,还需要图标处理程序来实现 IPersistFile,但事实并非如此。 对于图标处理程序, IPersistFile 现在是可选的,其他接口(如 IInitializeWithItem )是首选。

IShellExtInit 接口必须由以下项实现:

  • 快捷菜单处理程序
  • 拖放处理程序
  • 属性表处理程序

实现 IPersistFile

IPersistFile 接口旨在允许从磁盘文件加载对象或将其保存到磁盘文件。 除了 IUnknown 之外,它还具有六种方法(其自己的五种方法)以及它从 IPersist 继承的 GetClassID 方法。 使用 Shell 扩展时, IPersist 仅用于初始化 Shell 扩展处理程序对象。 由于通常不需要从磁盘读取或写入磁盘,因此只有 GetClassIDLoad 方法需要非ken 实现。

Shell 首先调用 GetClassID ,函数 (扩展处理程序对象的 CLSID) 返回类标识符。 然后,Shell 调用 Load 并传入两个值。 第一个 是 pszFileName,是一个 Unicode 字符串,其中包含 Shell 要操作的文件或文件夹的名称。 第二个是 dwMode,它指示文件访问模式。 由于通常不需要访问文件, 因此 dwMode 通常为零。 方法根据需要存储这些值,供以后参考。

以下代码片段演示了典型的 Shell 扩展处理程序如何实现 GetClassIDLoad 方法。 它旨在处理 ANSI 或 Unicode。 CLSID_SampleExtHandler是扩展处理程序对象的 GUID,CSampleExtHandler 是用于实现接口的类的名称。 m_szFileNamem_dwMode变量是专用变量,用于存储文件的名称和访问标志。

wchar_t m_szFileName[MAX_PATH];    // The file name
DWORD m_dwMode;                  // The file access mode

CSampleExtHandler::GetClassID(CLSID *pCLSID)
{
    *pCLSID = CLSID_SampleExtHandler;
}

CSampleExtHandler::Load(PCWSTR pszFile, DWORD dwMode)
{
    m_dwMode = dwMode;
    return StringCchCopy(_szFileName, ARRAYSIZE(m_szFileName), pszFile);
}

实现 IShellExtInit

除了 IUnknown 之外,IShellExtInit 接口只有一种方法 IShellExtInit::Initialize 方法具有三个参数,Shell 可以使用这些参数传递各种类型的信息。 传入的值取决于处理程序的类型,有些值可以设置为 NULL

  • pIDFolder 保存指向 PIDL) 项标识符列表的文件夹指针 (。 对于属性表扩展,它是 NULL。 对于快捷菜单扩展,它是包含显示其快捷菜单的项的文件夹的 PIDL。 对于非默认拖放处理程序,它是目标文件夹的 PIDL。
  • pDataObject 保存指向数据对象的 IDataObject 接口的指针。 数据对象保存 CF_HDROP格式的 一个或多个文件名。
  • hRegKey 保存文件对象或文件夹类型的注册表项。

IShellExtInit::Initialize 方法根据需要存储文件名、IDataObject 指针和注册表项,以供以后使用。 以下代码片段演示 了 IShellExtInit::Initialize 的实现。 为简单起见,此示例假定数据对象仅包含单个文件。 通常,它可能包含多个需要提取的文件。

LPCITEMIDLIST  m_pIDFolder;           //The folder's PIDL
wchar_t        m_szFile[MAX_PATH];    //The file name
IDataObject   *m_pDataObj;            //The IDataObject pointer
HKEY           m_hRegKey;             //The file or folder's registry key

STDMETHODIMP CShellExt::Initialize(LPCITEMIDLIST pIDFolder, 
                                   IDataObject *pDataObj, 
                                   HKEY hRegKey) 
{ 
    // If Initialize has already been called, release the old PIDL
    ILFree(m_pIDFolder);
    m_pIDFolder = nullptr;

    // Store the new PIDL.
    if (pIDFolder)
    {
        m_pIDFolder = ILClone(pIDFolder);
    }
    
    // If Initialize has already been called, release the old
    // IDataObject pointer.
    if (m_pDataObj)
    { 
        m_pDataObj->Release(); 
    }
     
    // If a data object pointer was passed in, save it and
    // extract the file name. 
    if (pDataObj) 
    { 
        m_pDataObj = pDataObj; 
        pDataObj->AddRef(); 
      
        STGMEDIUM   medium;
        FORMATETC   fe = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
        UINT        uCount;

        if (SUCCEEDED(m_pDataObj->GetData(&fe, &medium)))
        {
            // Get the count of files dropped.
            uCount = DragQueryFile((HDROP)medium.hGlobal, (UINT)-1, NULL, 0);

            // Get the first file name from the CF_HDROP.
            if (uCount)
                DragQueryFile((HDROP)medium.hGlobal, 0, m_szFile, 
                              sizeof(m_szFile)/sizeof(TCHAR));

            ReleaseStgMedium(&medium);
        }
    }

    // Duplicate the registry handle. 
    if (hRegKey) 
        RegOpenKeyEx(hRegKey, nullptr, 0L, MAXIMUM_ALLOWED, &m_hRegKey); 
    return S_OK; 
}

CSampleExtHandler 是用于实现接口的类的名称。 m_pIDFolderm_pDataObjectm_szFileNamem_hRegKey变量是用于存储传入的信息的私有变量。 为简单起见,此示例假定数据对象仅保留一个文件名。 从数据对象检索 FORMATETC 结构后, 使用 DragQueryFileFORMATETC 结构的 medium.hGlobal 成员中提取文件名。 如果传入注册表项,则 方法使用 RegOpenKeyEx 打开密钥,并将句柄分配给 m_hRegKey

信息提示自定义

可通过两种方式自定义信息提示:

  • 实现支持 IQueryInfo 的对象,然后在注册表中的适当子项下注册该对象 (请参阅下面的注册 shell 扩展处理程序) 。
  • 指定要显示的特定文件属性的固定字符串或列表。

若要显示命名空间扩展的固定字符串,请在命名空间扩展的 {CLSID} 键中创建名为 InfoTip 的条目。 将该条目的值设置为要显示的文本字符串(如本示例所示),或指定该资源 (中的资源和索引的间接字符串,以便) 本地化。

HKEY_CLASSES_ROOT
   CLSID
      {CLSID}
         InfoTip = InfoTip string for your namespace extension

若要显示某个文件类型的固定字符串,请在该文件类型的 ProgID 键中创建名为 InfoTip 的条目。 将该条目的值设置为要显示的文字字符串或指定该资源 (中的资源和索引的间接字符串,以便) 本地化,如本示例所示。

HKEY_CLASSES_ROOT
   ProgID
      InfoTip = Resource.dll, 3

如果希望 Shell 在特定文件类型的信息提示中显示特定文件属性,请在该文件类型的 ProgID 键中创建名为 InfoTip 的条目。 将该条目的值设置为规范属性名称、格式标识符 (FMTID) /property 标识符 (PID) 对的分号表示的列表,或两者兼而有之。 此值必须以“prop:”开头,才能将其标识为属性列表字符串。 如果省略“prop:”,则该值将被视为文本字符串并按此方式显示。

在以下示例中, propname 是规范属性名称, (如 System.Date) 和 {fmtid},pidFMTID/PID 对。

HKEY_CLASSES_ROOT
   ProgID
      InfoTip = prop:propname;propname;{fmtid},pid;{fmtid},pid

可以使用以下属性名称:

属性名称 说明 检索自
作者 文档的作者 PIDSI_AUTHOR
标题 文档的标题 PIDSI_TITLE
主题 主题摘要 PIDSI_SUBJECT
评论 文档注释 PIDSI_COMMENT 或文件夹/驱动程序属性
PageCount 页数 PIDSI_PAGECOUNT
名称 友好名称 标准文件夹视图
OriginalLocation 原始文件的位置 公文包文件夹和回收站文件夹
DateDeleted 删除日期文件 回收站文件夹
类型 文件类型 标准文件夹详细信息视图
大小 文件大小 标准文件夹详细信息视图
SyncCopyIn 与 OriginalLocation 相同 与 OriginalLocation 相同
修改时间 上次修改日期 标准文件夹详细信息视图
创建 创建日期 标准文件夹详细信息视图
已访问 上次访问日期 标准文件夹详细信息视图
InFolder 包含文件的目录 文档搜索结果
级别 搜索匹配的质量 文档搜索结果
FreeSpace 可用存储空间 磁盘驱动器
NumberOfVisits 访问次数 收藏夹文件夹
特性 文件属性 标准文件夹详细信息视图
Company 公司名称 PIDDSI_COMPANY
类别 文档类别 PIDDSI_CATEGORY
版权信息 媒体版权 PIDMSI_COPYRIGHT
HTMLInfoTipFile HTML 信息提示文件 文件夹Desktop.ini文件

 

使用 Shell 扩展处理程序增强 Windows 搜索

Shell 扩展处理程序可用于增强 Windows 搜索协议处理程序提供的用户体验。 若要启用此类增强功能,必须将支持的 Shell 扩展处理程序设计为与作为数据源的搜索协议处理程序集成。 有关如何通过与 Shell 扩展处理程序集成增强 Windows 搜索协议处理程序的信息,请参阅 添加图标、预览和快捷菜单。 有关 Windows 搜索协议处理程序的详细信息,请参阅 开发协议处理程序

注册 Shell 扩展处理程序

必须先注册 Shell 扩展处理程序对象,Shell 才能使用它。 本部分是有关如何注册 Shell 扩展处理程序的一般讨论。

每当创建或更改 Shell 扩展处理程序时,请务必通知系统你已使用 SHChangeNotify 进行了更改,并指定 SHCNE_ASSOCCHANGED 事件。 如果不调用 SHChangeNotify,则在重新启动系统之前,可能无法识别更改。

与所有 COM 对象一样,必须使用 UUIDGEN.exe 等工具为处理程序创建 GUID。 在 HKEY_CLASSES_ROOT\CLSID 下创建一个键,其名称是 GUID 的字符串形式。 由于 Shell 扩展处理程序是进程内服务器,因此必须在 GUID 键下创建 InProcServer32 密钥,并将默认值设置为处理程序 DLL 的路径。 使用单元线程模型。

每当 Shell 执行可能涉及 Shell 扩展处理程序的操作时,它都会检查相应的注册表项。 注册扩展处理程序的键可控制何时调用扩展处理程序。 例如,当 Shell 显示 文件类型成员的快捷菜单时,通常使用名为 的快捷菜单处理程序。 在这种情况下,处理程序必须在文件类型的 ProgID 密钥下注册。

处理程序名称

若要启用 Shell 扩展处理程序,请创建一个包含处理程序子项名称的子项, (请参阅下面的) shellEx 子项下的 shellEx 子项,该子项) 的 ProgID (或 预定义 Shell 对象的 Shell 对象 类型名称 () 。

例如,如果要为 MyProgram.1 注册快捷菜单扩展处理程序,请首先创建以下子项:

HKEY_CLASSES_ROOT
   MyProgram.1
      ShellEx
         ContextMenuHandlers

对于以下处理程序,在“处理程序子项名称”键下创建一个子项,其名称是 Shell 扩展的 CLSID 的字符串版本。 可以通过创建多个子项,在处理程序子项名称键下注册多个扩展。

Handler 接口 处理程序子项名称
快捷菜单处理程序 IContextMenu ContextMenuHandlers
Copyhook 处理程序 ICopyHook CopyHookHandlers
拖放处理程序 IContextMenu DragDropHandlers
属性表处理程序 IShellPropSheetExt PropertySheetHandlers
Windows Vista) 中已弃用列提供程序处理程序 ( IColumnProvider ColumnHandlers

 

对于以下处理程序,“处理程序子项名称”键的默认值是 Shell 扩展的 CLSID 的字符串版本。 只能为这些处理程序注册一个扩展。

Handler 接口 处理程序子项名称
数据处理程序 IDataObject DataHandler
删除处理程序 IDropTarget DropHandler
图标处理程序 IExtractIconA/W IconHandler
图像处理程序 IExtractImage {BB2E617C-0920-11d1-9A0B-00C04FC2D6C1}
缩略图图像处理程序 IThumbnailProvider {E357FCCD-A995-4576-B01F-234630154E96}
Infotip 处理程序 IQueryInfo {00021500-0000-0000-C000-0000-00000000046}
shell 链接 (ANSI ) IShellLinkA {000214EE-0000-0000-C000-000000000046}
shell 链接 (UNICODE) IShellLinkW {000214F9-0000-0000-C000-000000000046}
结构化存储 IStorage {0000000B-0000-0000-C000-000000000046}
元数据 IPropertyStore PropertyHandler
元数据 Windows Vista) 中弃用的 IPropertySetStorage ( PropertyHandler
固定到“开始”菜单 IStartMenuPinnedList {a2a9545d-a0c2-42b4-9708-a0b2badd77c8}
固定到任务栏 {90AA3A4E-1CBA-4233-B8BB-535773D48449}

 

只有包含 IsShortCut 条目的文件类型才需要指定用于将“固定到开始”菜单和“固定到任务栏”的子项。

Windows Vista 中删除了对列提供程序处理程序的支持。 此外,从 Windows Vista 开始, IPropertySetStorage 已弃用,改用 IPropertyStore

虽然 IExtractImage 仍受支持, 但 IThumbnailProvider 是 Windows Vista 及更高版本的首选。

预定义 Shell 对象

Shell 在 HKEY_CLASSES_ROOT 下定义了其他对象 这些对象可以采用与文件类型相同的方式进行扩展。 例如,若要为所有文件添加属性表处理程序,可以在 PropertySheetHandlers 键下注册。

HKEY_CLASSES_ROOT
   *
      shellex
         PropertySheetHandlers

下表提供了可在其中注册扩展处理程序的 HKEY_CLASSES_ROOT 的各种子项。 请注意,许多扩展处理程序不能在所有列出的子项下注册。 有关更多详细信息,请参阅特定处理程序的文档。

子项 说明 可能的处理程序 版本
* 所有文件 快捷菜单、属性表、谓词 (请参阅以下) All
AllFileSystemObjects 所有文件和文件夹 快捷菜单、属性表、谓词 4.71
文件夹 全部文件夹 快捷菜单、属性表、谓词 All
Directory 文件夹 快捷菜单、属性表、谓词 All
Directory\Background 文件文件夹背景 仅快捷菜单 4.71
驱动 MyComputer 中的所有驱动器,例如“C:\” 快捷菜单、属性表、谓词 All
Network “我的网络位置”下的整个网络 () 快捷菜单、属性表、谓词 All
Network\Type\# 类型为 # 的所有对象 (请参阅以下) 快捷菜单、属性表、谓词 4.71
NetShare 所有网络共享 快捷菜单、属性表、谓词 4.71
NetServer 所有网络服务器 快捷菜单、属性表、谓词 4.71
network_provider_name 网络提供程序“network_provider_name”提供的所有对象 快捷菜单、属性表、谓词 All
打印机 所有打印机 快捷菜单,属性表 All
AudioCD CD 驱动器中的音频 CD 仅谓词 All
Dvd WINDOWS 2000) (DVD 驱动器 快捷菜单、属性表、谓词 4.71

 

注意:

  • 可通过右键单击文件夹内的文件文件夹来访问文件文件夹背景快捷菜单,但不能通过文件夹的任何内容进行访问。
  • “谓词”是在 HKEY_CLASSES_ROOT\子键\Shell\谓词 下注册的特殊命令。
  • 对于 网络\类型\# ,“#”是十进制网络提供程序类型代码。 网络提供程序类型代码是网络类型的高字。 Winnetwk.h 头文件中提供了网络类型列表,) (WNNC_NET_* 值。 例如,WNNC_NET_SHIVA是0x00330000,因此相应的类型键将 HKEY_CLASSES_ROOT\网络\类型\51
  • network_provider_name”是 WNetGetProviderName 指定的网络提供程序名称,其中空格转换为下划线。 例如,如果安装了 Microsoft 网络提供商,则其提供程序名称为“Microsoft Windows 网络”,并且Microsoft_Windows_Network相应的network_provider_name

扩展处理程序注册示例

若要启用特定处理程序,请在扩展处理程序类型键下创建一个具有处理程序名称的子项。 Shell 不使用处理程序的名称,但它必须与该类型子项下的所有其他名称不同。 将名称子项的默认值设置为处理程序 GUID 的字符串形式。

以下示例演示了使用示例 .myp 文件类型启用快捷菜单和属性表扩展处理程序的注册表项:

HKEY_CLASSES_ROOT
   .myp
      (Default) = MyProgram.1
   CLSID
      {00000000-1111-2222-3333-444444444444}
         InProcServer32
            (Default) = C:\MyDir\MyCommand.dll
            ThreadingModel = Apartment
      {11111111-2222-3333-4444-555555555555}
         InProcServer32
            (Default) = C:\MyDir\MyPropSheet.dll
            ThreadingModel = Apartment
   MyProgram.1
      (Default) = MyProgram Application
      Shellex
         ContextMenuHandler
            MyCommand
               (Default) = {00000000-1111-2222-3333-444444444444}
         PropertySheetHandlers
            MyPropSheet
               (Default) = {11111111-2222-3333-4444-555555555555}

对于所有 Windows 系统,必须遵循本节中讨论的注册过程。

有关实现In-Process扩展的指南