如何实现 IContextMenu 接口

IContextMenu 是最强大但也是要实现的最复杂的接口。 强烈建议使用静态谓词方法之一实现谓词。 有关详细信息,请参阅 选择静态或动态快捷菜单方法 IContextMenu 有三种方法: GetCommandStringInvokeCommandQueryContextMenu,此处将对此进行详细介绍。

需要了解的事项

技术

  • C++

先决条件

  • 静态谓词
  • 快捷菜单

Instructions

IContextMenu::GetCommandString 方法

处理程序的 IContextMenu::GetCommandString 方法用于返回谓词的规范名称。 此方法是可选的。 在 Windows XP 和早期版本的 Windows 中,当 Windows 资源管理器具有状态栏时,此方法用于检索菜单项的状态栏中显示的帮助文本。

idCmd 参数保存调用 IContextMenu::QueryContextMenu 时定义的命令的标识符偏移量。 如果请求帮助字符串, uFlags 将设置为 GCS_HELPTEXTW。 将帮助字符串复制到 pszName 缓冲区,并将其强制转换为 PWSTR。 通过将 uFlags 设置为 GCS_VERBW 来请求谓词字符串。 将相应的字符串复制到 pszName,就像使用帮助字符串一样。 快捷菜单处理程序不使用 GCS_VALIDATEAGCS_VALIDATEW 标志。

以下示例演示 GetCommandString 的简单实现,该实现对应于本主题的 IContextMenu::QueryContextMenu 方法部分中给出的 QueryContextMenu 示例 由于处理程序只添加一个菜单项,因此只能返回一组字符串。 方法测试 idCmd 是否有效,如果有效,则返回请求的字符串。

StringCchCopy 函数用于将请求的字符串复制到 pszName,以确保复制的字符串不会超过 cchName 指定的缓冲区大小。 此示例仅实现对 uFlags 的 Unicode 值的支持,因为自 Windows 2000 以来,Windows 资源管理器中只使用这些值。

IFACEMETHODIMP CMenuExtension::GetCommandString(UINT idCommand, 
                                                UINT uFlags, 
                                                UINT *pReserved, 
                                                PSTR pszName, 
                                                UINT cchName)
{
    HRESULT hr = E_INVALIDARG;

    if (idCommand == IDM_DISPLAY)
    {
        switch (uFlags)
        {
            case GCS_HELPTEXTW:
                // Only useful for pre-Vista versions of Windows that 
                // have a Status bar.
                hr = StringCchCopyW(reinterpret_cast<PWSTR>(pszName), 
                                    cchName, 
                                    L"Display File Name");
                break; 

            case GCS_VERBW:
                // GCS_VERBW is an optional feature that enables a caller
                // to discover the canonical name for the verb that is passed in
                // through idCommand.
                hr = StringCchCopyW(reinterpret_cast<PWSTR>(pszName), 
                                    cchName, 
                                    L"DisplayFileName");
                break; 
        }
    }
    return hr;
}

IContextMenu::InvokeCommand 方法

当用户单击菜单项以告知处理程序运行关联的命令时,将调用此方法。 pici 参数指向包含运行命令所需的信息的 结构。

尽管 pici 在 Shlobj.h 中声明为 CMINVOKECOMMANDINFO 结构,但在实践中,它通常指向 CMINVOKECOMMANDINFOEX 结构。 此结构是 CMINVOKECOMMANDINFO 的扩展版本,具有多个附加成员,可用于传递 Unicode 字符串。

检查 picicbSize 成员以确定传入的结构。 如果它是 CMINVOKECOMMANDINFOEX 结构,并且 fMask 成员设置了 CMIC_MASK_UNICODE 标志,请将 pici 强制转换为 CMINVOKECOMMANDINFOEX。 这使应用程序能够使用结构的最后五个成员中包含的 Unicode 信息。

结构的 lpVerblpVerbW 成员用于标识要执行的命令。 命令通过以下两种方式之一进行标识:

  • 通过命令的谓词字符串
  • 按命令的标识符偏移量

若要区分这两种情况,检查 ANSI 事例的 lpVerb 高序字,对于 Unicode 大小写,则为 lpVerbW。 如果高序字非零, lpVerblpVerbW 将保留谓词字符串。 如果高序字为零,则命令偏移量位于 lpVerb 的低序字中。

以下示例演示 IContextMenu::InvokeCommand 的简单实现,该实现对应于本部分前后给出的 IContextMenu::QueryContextMenuIContextMenu::GetCommandString 示例。 方法首先确定要传入的结构。 然后,它确定命令是否由其偏移量或谓词标识。 如果 lpVerblpVerbW 保留有效的谓词或偏移量,该方法将显示一个消息框。

STDMETHODIMP CShellExtension::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
{
    BOOL fEx = FALSE;
    BOOL fUnicode = FALSE;

    if(lpcmi->cbSize == sizeof(CMINVOKECOMMANDINFOEX))
    {
        fEx = TRUE;
        if((lpcmi->fMask & CMIC_MASK_UNICODE))
        {
            fUnicode = TRUE;
        }
    }

    if( !fUnicode && HIWORD(lpcmi->lpVerb))
    {
        if(StrCmpIA(lpcmi->lpVerb, m_pszVerb))
        {
            return E_FAIL;
        }
    }

    else if( fUnicode && HIWORD(((CMINVOKECOMMANDINFOEX *) lpcmi)->lpVerbW))
    {
        if(StrCmpIW(((CMINVOKECOMMANDINFOEX *)lpcmi)->lpVerbW, m_pwszVerb))
        {
            return E_FAIL;
        }
    }

    else if(LOWORD(lpcmi->lpVerb) != IDM_DISPLAY)
    {
        return E_FAIL;
    }

    else
    {
        MessageBox(lpcmi->hwnd,
                   "The File Name",
                   "File Name",
                   MB_OK|MB_ICONINFORMATION);
    }

    return S_OK;
}

IContextMenu::QueryContextMenu 方法

Shell 调用 IContextMenu::QueryContextMenu ,使快捷菜单处理程序能够将其菜单项添加到菜单中。 它在 hmenu 参数中传入 HMENU 句柄。 indexMenu 参数设置为要用于要添加的第一个菜单项的索引。

处理程序添加的任何菜单项都必须具有 介于 idCmdFirstidCmdLast 参数中的值之间的标识符。 通常,第一个命令标识符设置为 idCmdFirst,对于每个附加命令,该标识符 (1) 递增。 这种做法有助于避免超过 idCmdLast ,并在 Shell 调用多个处理程序时最大化可用标识符的数量。

项标识符的 命令偏移量标识符与 idCmdFirst 中的值之间的差异。 存储处理程序添加到快捷菜单的每个项的偏移量,因为如果 Shell 随后调用 IContextMenu::GetCommandString 或 IContextMenu::InvokeCommand,则它可能会使用该偏移量来标识该项。

还应将 谓词 分配给添加的每个命令。 谓词是调用 InvokeCommand 时可用于标识命令的字符串,而不是偏移量。 ShellExecuteEx 等函数也使用它来执行快捷菜单命令。

可以通过与快捷菜单处理程序相关的 uFlags 参数传入三个标志。 下表对它们进行了说明。

标志 描述
CMF_DEFAULTONLY 用户已选择默认命令,通常通过双击对象。 IContextMenu::QueryContextMenu 应在不修改菜单的情况下将控制权返回到 Shell。
CMF_NODEFAULT 菜单中的项不应为默认项。 方法应将其命令添加到菜单中。
CMF_NORMAL 快捷菜单将正常显示。 方法应将其命令添加到菜单中。

 

使用 InsertMenuInsertMenuItem 将菜单项添加到列表。 然后返回严重性设置为SEVERITY_SUCCESS的 HRESULT 值。 将代码值设置为已分配的最大命令标识符的偏移量,加上一 (1) 。 例如,假设 idCmdFirst 设置为 5,并且你向菜单添加三个命令标识符为 5、7 和 8 的项。 返回值应为 MAKE_HRESULT (SEVERITY_SUCCESS、0、8 + 1) 。

以下示例演示插入单个命令的 QueryContextMenu 的简单实现。 命令的标识符偏移量IDM_DISPLAY,设置为零。 m_pszVerbm_pwszVerb变量是私有变量,用于以 ANSI 和 Unicode 格式存储与语言无关的关联谓词字符串。

#define IDM_DISPLAY 0

STDMETHODIMP CMenuExtension::QueryContextMenu(HMENU hMenu,
                                              UINT indexMenu,
                                              UINT idCmdFirst,
                                              UINT idCmdLast,
                                              UINT uFlags)
{
    HRESULT hr;
    
    if(!(CMF_DEFAULTONLY & uFlags))
    {
        InsertMenu(hMenu, 
                   indexMenu, 
                   MF_STRING | MF_BYPOSITION, 
                   idCmdFirst + IDM_DISPLAY, 
                   "&Display File Name");

    
        
        hr = StringCbCopyA(m_pszVerb, sizeof(m_pszVerb), "display");
        hr = StringCbCopyW(m_pwszVerb, sizeof(m_pwszVerb), L"display");

        return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(IDM_DISPLAY + 1));
    }

    return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(0));}

备注

有关其他谓词实现任务,请参阅 创建快捷菜单处理程序