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

上下文菜单 Shell 扩展 AppWizard

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.60/5 (7投票s)

2000年11月24日

viewsIcon

341189

downloadIcon

3106

用于简化实现上下文菜单 Shell 扩展的向导

本文旨在使实现上下文菜单外壳扩展变得微不足道。本文的灵感来源于Michael Dunn撰写的关于编写外壳扩展的极其出色的系列教程。我强烈建议您仔细阅读,以便对后续内容有一个良好的理解。

虽然我已经对ATL和C++有信心,但在此之前我对实现外壳扩展一无所知,但遵循Michael Dunn的指示却毫不费力,非常简单。然而,实现外壳扩展通常意味着一遍又一遍地输入相同的代码。这使其成为一项相当繁琐且容易出错的任务。

因此,向导是提供必要源代码骨架的理想候选者。

安装和运行向导

上下文菜单外壳扩展向导包含一个独立的CTXMENUAPP.AWX文件,需要将其复制到您的模板文件夹中。模板文件夹可以在您的主Microsoft Visual Studio安装文件夹下的\Common\MSDev98\Template文件夹中找到。

通过使用“文件|新建”菜单项激活向导,该菜单项会触发新建项目对话框。

 [VC New dialog - 5K]

选择上下文菜单外壳扩展项会显示以下向导对话框。此步骤允许您指定外壳扩展将调用的文件类型,以及向导将生成的C++类名。

 [AppWizard dialog - 10K]

向导提供什么?

该向导生成一个Visual Studio项目,类似于ATL/COM应用向导创建的项目。主要区别在于项目已经包含一个实现上下文菜单扩展骨架的类。

回顾Michael Dunn的教程,上下文菜单外壳扩展是一个COM对象,它应该实现IShellExtInitIContextMenu接口。向导生成的类已经提供了支持这些实现所需的一切,形式为两个头文件atlshellex.hatlctxmenu.h,我稍后将对此进行描述。

让我们从一个示例开始

让我们尝试编写一个类似于教程中描述的外壳扩展,看看它有多容易。从上面的屏幕截图可以看出,我们选择将我们的外壳扩展挂钩到.TXT文件,就像Michael的一样。还要注意,在我们示例中将实现扩展的类名为CShellExt

根据Michael的要求,我们需要在上下文菜单中添加一个命令,该命令将在右键单击.TXT文件时由外壳显示。该命令还应提供一个友好的快速状态帮助字符串。为了实现这一点,只需向您的项目添加两个字符串资源,一个用于菜单项文本本身,另一个用于快速帮助字符串。在此示例中,我们添加以下字符串

  • IDS_ITEM1: "SimpleShlExt Test Item"
  • IDS_DESC1: "This is the simple shell extension's help"

然后,我们需要在CShellExt类中提供对菜单项的支持。通过在项目窗口的ClassView选项卡中双击类名来打开类头文件。找到如下所示的上下文菜单映射

// IContextMenuImpl menu item maps
BEGIN_MENU_ITEM_MAP()
    // TODO: Add menu item entries
    // Menu item entries take
    // 1. a string resource identifier for the menu item name
    // 2. a string resource identifier for the menu item description
    // Ex: MENU_ITEM_ENTRY(IDS_ITEM1, IDS_DESC1)
END_MENU_ITEM_MAP()

注释清楚地说明了您需要执行哪些任务才能支持在外壳的上下文菜单中添加菜单项。对于我们的示例,只需取消注释MENU_ITEM_MAP中的最后一行,如下所示

      ...
    // 2. a string resource identifier for the menu item description
    MENU_ITEM_ENTRY(IDS_ITEM1, IDS_DESC1)
END_MENU_ITEM_MAP()

此时,我们拥有了一个功能齐全的外壳扩展,但我们还需要添加在选择菜单项时执行的代码。与Michael的示例一样,我们将显示右键单击的文件名。

CShellExt声明中找到菜单命令映射

BEGIN_MENU_COMMAND_MAP()
    // TODO: Add menu command entries
    // Menu command entries take:
    // 1. a string resource identifier for the menu item name
    // 2. the name of a member function triggered upon selection
    // Ex: MENU_COMMAND_ENTRY(IDS_ITEM1, OnItem1)
END_MENU_COMMAND_MAP()

同样,注释清楚地说明了您的职责。对于我们的示例,我们将取消注释最后一行,如下所示

    ...
    // 2. the name of a member function triggered upon selection
    MENU_COMMAND_ENTRY(IDS_ITEM1, OnItem1)
END_MENU_COMMAND_MAP()

最后一部分是实际提供菜单项处理程序的实现,通过编写OnItem1函数。我们将这些行添加到类声明中

// Menu item handlers
protected:
    // TODO: Add menu item handlers
    // Menu item handlers bear the following signature
    // Ex: HRESULT OnItem1(LPCMINVOKECOMMANDINFO lpici);
    HRESULT OnItem1(LPCMINVOKECOMMANDINFO lpici)
    {
        TCHAR szText[1024];
        lstrcpy(szText, _T("The selected file was:\n\n"));
        lstrcat(szText, m_files[0]);

        ::MessageBox(lpici->hwnd, szText, _T("SimpleShlExt"), 
                             MB_ICONINFORMATION);
        return S_OK;
    }

我们完成了!要进行尝试,只需编译外壳扩展。启动Windows Explorer并尝试右键单击.TXT文件……好了。我们拥有一个功能齐全的上下文菜单外壳扩展,它会自动为我们注册到正确的文件类型,并允许我们专注于外壳扩展的功能。

它是如何工作的?

如果您注意到了为OnItem1处理程序添加的几行代码,您可能会注意到神秘的m_files[0]成员变量。它从哪里来?

您还注意到外壳扩展已自动为我们注册到正确的文件扩展名。如果您还记得Michael的文章,这涉及到处理.RGS文件,并且处理起来并不容易。那么它是如何实现的呢?

答案在于两个头文件atlshellex.hatlctxmenu.h,它们作为向导创建的一部分被静默地包含在您的外壳扩展中。这些文件提供了实现上述所需COM接口IShellExtInitIContextMenu的基础架构。

IShellExtInitImpl

如果您回顾CShellExt类声明,您可能会注意到以下代码行

#include "atlshellex.h"

//////////////////////////////////////////////////////////////
// CShellExt
class ATL_NO_VTABLE CShellExt : 
    ...
    public IShellExtInitImpl,
{
DECLARE_REGISTRY_CONTEXTMENU_EXTENSION(IDR_CSHELLEXT, txt, 
                                       SimpleShlExt, CLSID_CShellExt)
...
// IShellExtInitImpl override
protected:
     bool IsValidFile(CString& aFile) const { return TRUE; }
...

事实上,您的ATL类从IShellExtInitImpl多重继承,该类以纯ATL风格实现了IShellExtInit接口的部分功能。这个类通过DECLARE_REGISTRY_CONTEXTMENU_EXTENSION宏提供对外壳扩展自动注册的支持。

这个类负责维护在Shell中右键单击时选择的文件数组。这就是上面讨论的神秘的m_files变量。事实上,外壳扩展可以同时处理多个文件。因此,我们需要跟踪所有这些文件。

您可以通过覆盖类中的IsValidFile成员函数来定制此类的行为。请注意,向导创建了一个简单的此覆盖函数实现,它只是返回TRUE,无论是什么文件。这意味着所有文件都将被视为有效并添加到m_files数组中。通过此函数,您可以测试文件,然后再决定是否将其插入,从而更改行为。如果您不希望将文件添加到数组中,只需返回FALSE。

IContextMenuImpl

再次查看CShellExt类声明,也会发现IContextMenuImpl<CShellExt>的多重继承。

#include "atlctxmenu.h"

////////////////////////////////////////////////////////////////////////////
// CShellExt
class ATL_NO_VTABLE CShellExt : 
    ...
    public IContextMenuImpl<CShellExt>
{
...
// Menu item handlers
protected:
    HRESULT OnItem1(LPCMINVOKECOMMANDINFO lpici)
    {
        TCHAR szText[1024];
        lstrcpy(szText, _T("The selected file was:\n\n"));
        lstrcat(szText, m_files[0]);

        ::MessageBox(lpici->hwnd, szText, _T("SimpleShlExt"), 
                             MB_ICONINFORMATION);
        return S_OK;
    }

// IContextMenuImpl menu item maps
BEGIN_MENU_ITEM_MAP()
    MENU_ITEM_ENTRY(IDS_ITEM1, IDS_DESC1)
END_MENU_ITEM_MAP()

BEGIN_MENU_COMMAND_MAP()
    MENU_COMMAND_ENTRY(IDS_ITEM1, OnItem1)
END_MENU_COMMAND_MAP()
};

IContextMenuImpl类负责实现IContextMenu接口。它将遍历您提供的菜单项菜单命令映射,并将选择代码路由到相应的菜单项处理程序。

结论

正如您所见,实现上下文菜单扩展现在快速而轻松。此外,您可以在自己的项目中使用提供的atlctxmenu.hatlshellex.h文件。前一个文件atlshellex.h以一种可能直接与实现属性页外壳扩展相关的方式实现了IShellExtInit接口,但我将此留给读者作为练习。

我希望这篇文章足够清楚。如果您有任何问题,请告诉我。

历史

2002年11月20日 - 更新下载

© . All rights reserved.