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

Mini Shell 扩展框架 - 第 II 部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.48/5 (6投票s)

2005 年 1 月 26 日

5分钟阅读

viewsIcon

60173

downloadIcon

778

讨论一个用于创建 Windows Shell 扩展 (IContextMenuImpl) 的小型 C++ 框架。

目录

引言

Windows Shell 提供了用新菜单选项扩展右键菜单的功能。要启用此功能,Shell 扩展必须为特定的文件扩展名或全局“*”扩展名注册一个 IContextMenu 接口。本文讨论的 Mini Shell 框架提供了一组辅助类,可用于创建此类 Shell 扩展。IContextMenuImpl 类提供了以下功能:

  • 一个嵌入式类 CMenu,可以轻松创建正确的菜单项。
  • 一个注册系统,它会将属主绘制的菜单事件转发给“绘制”处理程序,并将菜单项命令选择到正确的命令处理程序。
  • 框架提供了一个 CSmallBitmapHandler 类,用于创建菜单项,并在菜单文本前添加一个小位图。

我最初的计划是在一篇文章中同时介绍 IContextMenuImplIShellFolderImpl。在完成 IShellFolderImpl 的最终确定过程中,我决定将这两个类的描述分成两篇文章。这样做的主要原因是 IShellFolderImpl 非常复杂,本身就需要大量篇幅来描述(将在第三部分介绍)。有关设计模型和使用的 .vvv 文件样本的更多背景信息,读者可以参考 Mini Shell 扩展框架的第一篇文章。

框架要求

框架的要求与第一篇文章相同。VC.NET 2002 + 最新 SDK 或 VC.NET 2003。该框架已在 Win 98、ME、W2K 和 XP 上进行了验证。

创建右键菜单扩展

与所有其他 Shell 扩展一样,右键菜单扩展也是一个 COM 对象。因此,第一个要求是注册 COM 类。该框架包含两个 ATL 注册脚本来实现此目的。一个注册脚本用于通常与应用程序一起打开的文件,另一个脚本应在文件在资源管理器中打开时使用(与 ShellFolder 扩展结合使用)。随附的 VVV 样本还演示了一个 ShellFolder 扩展,因此使用了 contextmenu_sf.rgs 注册脚本。

class ATL_NO_VTABLE CContextMenu :
  public CComObjectRootEx<CComSingleThreadModel>,
  public CComCoClass<CContextMenu, & __uuidof(CContextMenu)>,
  public IContextMenuImpl<CContextMenu>
{
public:

static HRESULT WINAPI UpdateRegistry(BOOL bRegister) throw()
{
  return IContextMenuImpl<CContextMenu>::UpdateRegistry(IDR_CONTEXTMENU, 
    bRegister, L"Sample ShellExtension ContextMenu",
    __uuidof(CShellFolder), wszVVVExtension);
}

上面的代码示例显示了类定义和 ATL 将调用以注册和注销扩展的静态函数 UpdateRegistry。此静态函数通常使用 ATL 类向导自动添加的 DECLARE_REGISTRY_RESOURCEID 宏来实现。为了指示 ATL 我们的对象支持哪些接口,必须设置一个“COM_MAP”。下面的代码显示了 VVV 样本选择支持的接口。由于 VVV 样本使用了属主绘制菜单项,因此需要 IContextMenu2IContextMenu3 接口。

BEGIN_COM_MAP(CContextMenu)
  COM_INTERFACE_ENTRY(IShellExtInit)
  COM_INTERFACE_ENTRY(IContextMenu)
  COM_INTERFACE_ENTRY(IContextMenu2)
  COM_INTERFACE_ENTRY(IContextMenu3)
END_COM_MAP()

框架的基类 IContextMenuImpl<> 必须被通知需要处理哪些扩展。

CContextMenu()
{
  RegisterExtension(_T(“.vvv”));
}

必须实现的最后一个必需函数是 OnQueryContextMenu。框架在收到 Shell 通过 COM 函数 QueryContextMenu 的调用后会调用此函数。下面的代码显示了 VVV 样本的实现。第一步检查用户选择的文件是否仅包含 .vvv 文件。第二步,扩展检查是否只选择了一个文件(理论上,只需要一个检查)。创建的第一个条目是一个带有属主绘制处理程序的子菜单。此新子菜单中添加了两个菜单项。CSmallBitmapHandler 类由框架提供。CEditWithNotepadCommandCAboutMSFCommand 是 VVV 样本的命令处理程序。

// Purpose: called by the 'impl' class. Request to configure the menu
void OnQueryContextMenu(IContextMenuImpl<CContextMenu>::CMenu& menu,
                        const std::vector<CString>& filenames)
{
  if (ContainsUnknownExtension(filenames))
    return; // only extend the menu when
            // only .vvv files are selected.

  if (filenames.size() != 1)
    return; // only add to the context menu when 1 file is selected.

  CMenu menuVVV = 
    menu.AddSubMenu(IDS_CONTEXTMENU_VVV_SUBMENU_HELP,
                    CCustomMenuHandlerPtr(new
                    CSmallBitmapHandler(IDS_CONTEXTMENU_VVV_SUBMENU,
                    IDB_MENUICON)));

  menuVVV.AddItem(IDS_CONTEXTMENU_EDIT_WITH_NOTEPAD,
                  IDS_CONTEXTMENU_EDIT_WITH_NOTEPAD_HELP,
                  CContextCommandPtr(new CEditWithNotepadCommand()));

  menuVVV.AddItem(IDS_CONTEXTMENU_ABOUT_MSF_HELP,
                  CContextCommandPtr(new CAboutMSFCommand()),
                  CCustomMenuHandlerPtr(new
                  CSmallBitmapHandler(IDS_CONTEXTMENU_ABOUT_MSF,
                                      IDB_MENUICON)));
}

命令 Functor

框架使用 functor 将选定的菜单项作为操作转发给 Shell 扩展。这些 functor 对象必须在创建菜单项时作为参数之一传递。样本使用的 functor CEditWithNotepadCommand 将启动记事本来编辑选定的 .VVV 文件。使用的 CreateProcess 函数是 Win32 CreateProcess 函数的“简短”版本,是框架提供的实用函数之一。

class CEditWithNotepadCommand : public CContextCommand
{
public:
  virtual void operator()(const CMINVOKECOMMANDINFO* /* pici */,
                          const std::vector<CString> & filenames)
  {
    ATLASSERT(filenames.size() == 1); // can only handle 1 file.

    // Use the command line param to pass 
    // the exe filename. This cause
    // Windows to use the path to find notepad.
    CString strCmd = _T("notepad.exe \"") + filenames[0] + _T("\"");

    CreateProcess(NULL, strCmd.GetBuffer());
  }
};

另一个 CAboutMSFCommand functor 更简单。它只是格式化一个字符串并将其显示给用户。调用 IsolationAwareMessageBox 消息框函数以使用新的 XP 样式(在 XP 上运行时)。提示:MessageBox 是隔离感知启用时 SDK 标头未重新定义的函数之一,必须显式调用。

class CAboutMSFCommand : public CContextCommand
{
public:
  virtual void operator()
    (const CMINVOKECOMMANDINFO* pici,
     const std::vector<CString> & /* filenames */)
  {
    CString strText;
    strText.Format(IDS_CONTEXTMENU_ABOUT_MASK,
                   HIWORD(MSF_VER), LOWORD(MSF_VER));

    IsolationAwareMessageBox(pici->hwnd, strText,
      LoadString(IDS_CONTEXTMENU_CAPTION), MB_OK);
  }
};

属主绘制菜单

在 Windows 的历史中,基本的菜单系统变化很小。为了提供“更酷”的外观,许多应用程序会绘制自己的菜单。Windows Shell 菜单本身仅为“打开方式”和“发送到”子菜单使用图像。右键菜单扩展也可以使用属主绘制菜单项。有三种选项可以将图形添加到菜单项:

  • 属主绘制。扩展可以完全自由地绘制菜单本身。
  • 使用小菜单位图。菜单项可以在文本前使用一个小位图。系统菜单中“关闭”前的 X 标记是一个很好的例子(并非在所有 Windows 版本上都支持)。
  • 使用自定义复选标记。

CSmallBitmapHandler

框架提供了支持类 CSmallBitmapHandler,可用于向菜单项添加小位图(13x13)。此类使用自定义复选标记选项在文本前添加小位图。此技术的主要优点是菜单项的文本与所有其他菜单项的绘制方式相同。这样可以防止“添加”的菜单项看起来不同或奇怪。完全属主绘制的项和位图菜单项最好保留给由扩展完全控制的子菜单。

历史

  • 0.87

    IContextMenuImpl 的改进版本。增加了对属主绘制菜单项的支持。

  • 0.85

    IContextMenuImpl 的初始版本。

© . All rights reserved.