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

如何在上下文菜单 Shell 扩展中使用子菜单

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (37投票s)

2003年2月15日

2分钟阅读

viewsIcon

286177

downloadIcon

3002

如何管理上下文菜单扩展中的子菜单

上下文菜单扩展中的子菜单

在本文中,我将介绍上下文菜单扩展的一个棘手方面——子菜单。大多数人在一开始尝试创建子菜单时,会导致 Explorer 出现奇怪的行为,但一旦你掌握了让 Explorer 正确管理菜单的技巧,就很容易了!本文假定你对上下文菜单扩展有很好的了解。如果需要复习,请参阅我的 Shell 扩展系列文章的第一部分第二部分

添加子菜单

本文的扩展是一个简单的“另存为...”子菜单,其中包含两个项目,“记事本”“Internet Explorer”。它的行为类似于 XP 中的增强型“另存为”菜单,并在你选择的程序中打开所选文件。这个“另存为”菜单将演示如何在扩展中正确创建子菜单。

你可能首先尝试的方法

首先想到的方法是使用 CreatePopupMenu() 创建一个新的菜单,并将其插入到 Explorer 提供的菜单中。

HRESULT COpenWithCtxMenuExt::QueryContextMenu ( 
  HMENU hmenu, UINT uMenuIndex, UINT uidFirstCmd, UINT uidLastCmd, UINT uFlags )
{
    // If the flags include CMF_DEFAULTONLY then we shouldn't do anything.
    if ( uFlags & CMF_DEFAULTONLY )
        return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 0 );

    // First, create and populate a submenu.
    HMENU hSubmenu = CreatePopupMenu();
    UINT uID = uidFirstCmd;

    InsertMenu ( hSubmenu, 0, MF_BYPOSITION, uID++, _T("&Notepad") );
    InsertMenu ( hSubmenu, 1, MF_BYPOSITION, uID++, _T("&Internet Explorer") );

    // Insert the submenu into the ctx menu provided by Explorer.
    InsertMenu ( hmenu, uMenuIndex, MF_BYPOSITION | MF_POPUP, 
                 (UINT_PTR) hSubmenu, _T("C&P Open With") );

    return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, uID - uidFirstCmd );
}

这对于上下文菜单来说实际上工作得很好,但是你添加的上下文菜单项会在 Explorer 的 “文件”菜单中重复出现。如果你反复调用上下文菜单,你会在 “文件”菜单上看到残留的弹出窗口。

 [Leftover submenus - 12K]

原因与 Explorer 在调用扩展后清理菜单的方式有关。QueryContextMenu() 的返回值告诉 Explorer 我们添加了多少项,Explorer 通过计算我们项的 ID 并删除它们来进行清理。Explorer 知道第一个项的 ID(因为它将该值作为 uidCmdFirst 传递给我们),并且可以计算其他项的 ID,因为假定这些 ID 是连续的。但是,弹出菜单没有 ID,因此 Explorer 不会删除它。

正确的方法

秘诀是使用 InsertMenuItem() API 插入子菜单。使用 InsertMenuItem() 的不同之处在于,你可以为子菜单指定一个 ID,而使用 InsertMenu() 时这是不可能的。这是更正后的 QueryContextMenu() 代码。

HRESULT COpenWithCtxMenuExt::QueryContextMenu ( 
  HMENU hmenu, UINT uMenuIndex, UINT uidFirstCmd, UINT uidLastCmd, UINT uFlags )
{
    // If the flags include CMF_DEFAULTONLY then we shouldn't do anything.
    if ( uFlags & CMF_DEFAULTONLY )
        return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 0 );

    // First, create and populate a submenu.
    HMENU hSubmenu = CreatePopupMenu();
    UINT uID = uidFirstCmd;

    InsertMenu ( hSubmenu, 0, MF_BYPOSITION, uID++, _T("&Notepad") );
    InsertMenu ( hSubmenu, 1, MF_BYPOSITION, uID++, _T("&Internet Explorer") );

    // Insert the submenu into the ctx menu provided by Explorer.
    MENUITEMINFO mii = { sizeof(MENUITEMINFO) };

    mii.fMask = MIIM_SUBMENU | MIIM_STRING | MIIM_ID;
    mii.wID = uID++;
    mii.hSubMenu = hSubmenu;
    mii.dwTypeData = _T("C&P Open With");

    InsertMenuItem ( hmenu, uMenuIndex, TRUE, &mii );

    return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, uID - uidFirstCmd );
}

这次的返回值是 3,它告诉 Explorer 我们插入了 3 个项目(两个“另存为”项目,以及子菜单本身)。由于所有 3 个项目都有 ID,Explorer 可以删除它们,并完全从其菜单中删除我们的项目。

© . All rights reserved.