65.9K
CodeProject 正在变化。 阅读更多。
Home

如何在 3 个简单步骤中将图标叠加到现有的 Shell 对象上

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.59/5 (23投票s)

2004年6月22日

6分钟阅读

viewsIcon

413948

downloadIcon

5671

如何实现一个图标叠加标识符。

引言

您是否曾想过如何将小图标绘制/叠加在现有图标上?或者如何让某些文件显示一个已更改的图标来表示特定状态?甚至想过快捷方式是如何在您的应用程序图标上显示小箭头的?我曾有过这些疑问,但在做了一些研究后,我发现这可以通过“Shell Icon Overlay Identifier”(Shell 图标叠加标识符)轻松实现。

简而言之,图标叠加通常是放置在 Windows 资源管理器或桌面上现有 Shell 对象图标上的一些小图标。一个广为人知的例子就是叠加在现有对象图标上的快捷方式箭头图标。Shell 对象可以是物理文件、命名空间、快捷方式等。

在本文中,您将学会如何非常轻松地实现一个 Shell 图标叠加标识符。无需对 Shell 扩展或 Shell 编程有任何先验知识。但是,要获得 Shell 编程的基本知识,请查阅本文的参考文献。

最好您拥有 Visual Studio .NET 和 Platform SDK,因为本文描述的步骤是针对 VS.NET 的,如果您使用 VS 6.0,它们也可能对您有用。

为了方便您,文章最后有一个全面的问答部分。

第一步 - 创建一个简单的 COM 对象

启动 Visual Studio .NET 后

  1. 选择“文件”菜单 / “新建项目”。
  2. 选择“Visual C++ 项目” / “ATL 项目”。
  3. 将项目命名为“OverlayIcon”,然后按“确定”。

现在,在 ATL 项目向导中

  1. 选择“应用程序设置”选项卡。
  2. 取消选中“属性”。
  3. 确保您选择的服务器类型是“动态链接库”。
  4. 如果您想获得 MFC 支持,请选中其选项(在本文中,我们不需要 MFC)。
  5. 现在按“完成”。

现在,在 VC 的类视图或使用“项目”菜单

  1. 右键单击类视图中的“OverlayIcon”树根。
  2. 然后选择“添加” / “新建类”。
  3. 您可以通过“项目菜单”/“添加新类”来实现(1)和(2)。
  4. 展开“Visual C++”树以查看“ATL”节点。
  5. 单击“ATL”,然后从右侧视图中选择“ATL Simple Object”,然后按“打开”。

现在,在“ATL Simple Object Wizard”窗口中

  1. 在“C++ / 短名称”文本框中,键入“MyOverlayIcon”。
  2. 您会注意到其他所有文本框也已填写。
  3. 现在,按“完成”。

此时,您应该在类视图中看到一个名为 CMyOverlayIcon 的新类。

在继续第二步(实现 IShellIconOverlayIdentifier 接口)之前,您需要向资源中添加一个新图标。此图标应在大部分区域透明,只有一个小区域会叠加在现有对象图标之上。提供的图标在其右下角有一个小的“a”。

第二步 - 实现 Shell 图标叠加标识符接口

现在,打开 MyOverlayIcon.h,并按照下面的粗体指示进行调整/添加条目。

// MyOverlayIcon.h : Declaration of the CMyOverlayIcon
#pragma once
#include "resource.h" // main symbols
#include "OverlayIcon.h"

// You can put these includes in "stdafx.h" if you want
#include <shlobj.h>
#include <comdef.h>

// CMyOverlayIcon
class ATL_NO_VTABLE CMyOverlayIcon : 
  public CComObjectRootEx<CComSingleThreadModel>,
  public CComCoClass<CMyOverlayIcon, &CLSID_MyOverlayIcon>,
  public IShellIconOverlayIdentifier,  
  public IDispatchImpl<IMyOverlayIcon, 
         &IID_IMyOverlayIcon, &LIBID_OverlayIconLib, 
  /*wMajor =*/ 1, /*wMinor =*/ 0>
  {
  public:
  CMyOverlayIcon()
  {
  }
  
  // IShellIconOverlayIdentifier Methods
  STDMETHOD(GetOverlayInfo)(LPWSTR pwszIconFile, 
           int cchMax,int *pIndex,DWORD* pdwFlags);
  STDMETHOD(GetPriority)(int* pPriority);
  STDMETHOD(IsMemberOf)(LPCWSTR pwszPath,DWORD dwAttrib);
  
DECLARE_REGISTRY_RESOURCEID(IDR_MYOVERLAYICON)

  BEGIN_COM_MAP(CMyOverlayIcon)
  COM_INTERFACE_ENTRY(IMyOverlayIcon)
  COM_INTERFACE_ENTRY(IDispatch)
  COM_INTERFACE_ENTRY(IShellIconOverlayIdentifier)  
  END_COM_MAP()

  DECLARE_PROTECT_FINAL_CONSTRUCT()
  HRESULT FinalConstruct()
  {
    return S_OK;
  }
  
  void FinalRelease() 
  {
  }
public:
};

OBJECT_ENTRY_AUTO(__uuidof(MyOverlayIcon), CMyOverlayIcon)

需要实现的三种方法是

方法 描述
GetOverlayInfo

提供图标叠加位图的位置。

此方法在初始化时首先被调用,它返回包含图标叠加图像的文件的完全限定路径,以及该文件中的零基索引。图标可以包含在任何标准文件类型中,包括 .exe.dll.ico

GetPriority

指定图标叠加的优先级。

此方法仅在初始化时调用。它为处理程序的图标叠加分配一个优先级值(范围从 0=最高优先级到 100=最低优先级)。

优先级有助于解决多个处理程序安装时的冲突。

IsMemberOf

指定是否应将图标叠加添加到 Shell 对象的图标。

您可以返回 S_OK 以允许添加叠加图标,或返回 S_FALSE 以保持对象图标不变。

在此示例中,我们的 IsMemberOf() 仅检查文件是否包含字符串“CodeProject”,如果是,则应用我们的图标叠加。您可以根据需要更改此条件。

现在,打开 MyOverlayIcon.cpp 来实现 IShellIconOverlayIdentifier 方法。

// MyOverlayIcon.cpp : Implementation of CMyOverlayIcon

#include "stdafx.h"
#include "MyOverlayIcon.h"


// CMyOverlayIcon
// IShellIconOverlayIdentifier::GetOverlayInfo
// returns The Overlay Icon Location to the system
STDMETHODIMP CCOverlayProvider::GetOverlayInfo(
             LPWSTR pwszIconFile,
             int cchMax,
             int* pIndex,
             DWORD* pdwFlags)
{
  // Get our module's full path
  GetModuleFileNameW(_AtlBaseModule.GetModuleInstance(), pwszIconFile, cchMax);

  // Use first icon in the resource
  *pIndex=0; 

  *pdwFlags = ISIOI_ICONFILE | ISIOI_ICONINDEX;
  return S_OK;
}

// IShellIconOverlayIdentifier::GetPriority
// returns the priority of this overlay 0 being the highest. 
STDMETHODIMP CCOverlayProvider::GetPriority(int* pPriority)
{
  // we want highest priority 
  *pPriority=0;
  return S_OK;
}

// IShellIconOverlayIdentifier::IsMemberOf
// Returns whether the object should have this overlay or not 
STDMETHODIMP CCOverlayProvider::IsMemberOf(LPCWSTR pwszPath, DWORD dwAttrib)
{
  wchar_t *s = _wcsdup(pwszPath);
  HRESULT r = S_FALSE;
  
  _wcslwr(s);

  // Criteria
  if (wcsstr(s, L"codeproject") != 0)
    r = S_OK;

  free(s);

  return r;
}

第三步 - 注册接口

除了常规的 COM 注册外,您还必须在此处注册此图标叠加处理程序

HKEY_LOCAL_MACHINE\Software\Microsoft\Windows 
   \CurrentVersion\Explorer\ShellIconOverlayIdentifiers

在那里,您创建一个新的子项(例如命名为 MyOverlayIcon),并将其默认值设置为对象类标识符(CLSID)全局唯一标识符(GUID)的字符串形式。

另一种执行此操作的方法是使用项目适当的 *.RGS 文件,该文件将自动处理注册/取消注册。

为了实现这一点,请打开 MyOverlayIcon.rgs 并添加下面粗体显示的行。

HKCR
{
    OverlayIcon.MyOverlayIcon.1 = s 'MyOverlayIcon Class'
    {
        CLSID = s '{81539FE6-33C7-4CE7-90C7-1C7B8F2F2D40}'
    }
    OverlayIcon.MyOverlayIcon = s 'MyOverlayIcon Class'
    {
        CLSID = s '{81539FE6-33C7-4CE7-90C7-1C7B8F2F2D40}'
        CurVer = s 'OverlayIcon.MyOverlayIcon.1'
    }
    NoRemove CLSID
    {
        ForceRemove {81539FE6-33C7-4CE7-90C7-1C7B8F2F2D40} = s 'MyOverlayIcon Class'
        {
            ProgID = s 'OverlayIcon.MyOverlayIcon.1'
            VersionIndependentProgID = s 'OverlayIcon.MyOverlayIcon'
            ForceRemove 'Programmable'
            InprocServer32 = s '%MODULE%'
            {
                val ThreadingModel = s 'Apartment'
            }
            val AppID = s '%APPID%'
            'TypeLib' = s '{ADF1FA2A-6EAA-4A97-A55F-3C8B92843EF5}'
        }
    }
}


HKLM
{
  NoRemove SOFTWARE
  {
    NoRemove Microsoft
    {
      NoRemove Windows
      {
        NoRemove CurrentVersion
        {
          NoRemove Explorer
          {
            NoRemove ShellIconOverlayIdentifiers
            {
              ForceRemove MyOverlayIcon = s '{81539FE6-33C7-4CE7-90C7-1C7B8F2F2D40}'
              {
              }           
            }
          }
        }
      }
    }
  }
}

请注意,我们添加了一个完整的块,从 HKLM(HKEY_LOCAL_MACHINE)开始,并在最后添加一个名为“MyOverlayIcon”的新子项,其默认值为我们对象的 GUID。如果您创建一个新项目,您肯定会有一个新的 GUID,请使用该 GUID 而不是文章中提供的 GUID。

保存 RGS 文件,编译项目(这将自动执行注册),然后看看图标现在如何被修改!

问答

提问 答案
我将 DLL 复制到了一台新计算机上,如何激活它?

您只需注册 DLL 即可。

但是,请确保您将所需的依赖项与 DLL 一起分发。

我收到一个错误:“无法写入 DllXYZ.dll”,是什么问题?

由于此 DLL 由 Explorer.exe 或使用 SHELL 的其他应用程序加载,因此您需要关闭所有已创建您的 DLL 实例的应用程序。

请查看 这篇文章,其中详细介绍了如何处理此类问题。

好的,我想删除图标叠加,该怎么做? 您只需取消注册您的 DLL。例如,尝试使用 regsvr32.exe /u myDll.dll
我无法编译代码,因为它出现警告:“error C2787: 'IShellIconOverlayIdentifier' : no GUID has been associated with this object”。

要解决此问题,您需要利用 Platform SDK 头文件。请通过以下方式执行:

  • “工具”菜单 / “选项”。
  • 选择“项目”节点。
  • 单击“VC++ Directories”。
  • 从“Show directories for”组合框中,选择“Include files”。
  • 单击列表中包含“PlatformSDK\include”的项目,并将其移到列表顶部,使其具有更高的包含优先级。
  • 单击“确定”,然后尝试重新编译。
如何为不同的条件设置不同的叠加图标?

据我所知,您需要创建另一个实现 IShellIconOverlayIdentifier 的 COM 对象。但是,这个新对象可以驻留在同一个项目中。

或许 这篇文章 会对您有所帮助。

参考

除了下面的参考文献,我还想感谢我在工作中的朋友兼同事(您知道您是谁)。

历史

  • 2004/06/20 - 初始版本。
  • 2004/07/01 - 更新了文章标题和屏幕截图。
© . All rights reserved.