c++windowswinapiwindows-shell

Correct way to implement IContextMenu


I'm trying to implement IContextMenu, but I'm having some difficulties. In the QueryContextMenu method I add the menu like this:

HRESULT QueryContextMenu(
    HMENU hmenu,
    UINT  indexMenu,
    UINT  idCmdFirst,
    UINT  idCmdLast,
    UINT  uFlags
)
{
    if (uFlags & CMF_DEFAULTONLY)
    {
        return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 0);
    }

    HMENU hSubMenu1 = CreatePopupMenu();
    AppendMenu(hSubMenu1, MF_STRING, idCmdFirst + 1, L"Submenu Item 1");
    AppendMenu(hSubMenu1, MF_STRING, idCmdFirst + 2, L"Submenu Item 2");
    AppendMenu(hmenu, MF_STRING | MF_POPUP, (UINT_PTR)hSubMenu1, L"Top-level menu");

    return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 3);
}

Now when the user selects a menu item, I need to perform the appropriate actions in the InvokeCommand method. For now, I determine the selected menu item like this:

HRESULT InvokeCommand(
    CMINVOKECOMMANDINFO* pici
)
{
    auto selectedMenuID = LOWORD(pici->lpVerb); // 1 or 2

    return S_OK;
}

Regarding the above code I have the following questions:

  1. In the InvokeCommand method, the menu ID is defined as auto selectedMenuID = LOWORD(pici->lpVerb). How reliable is this approach? I haven't found a clear definition for this in the documentation. Should I also check LOWORD for lpVerbW or lpVerb is sufficient?

  2. Some IContextMenu implementations implement adding a submenu not in the QueryContextMenu method, but in the IContextMenu3::HandleMenuMsg2 method (by handling WM_INITMENUPOPUP message). It turns out that we have two ways to add a submenu. How to choose the right way to add a submenu? What should you pay attention to in this case?


Solution

    1. There is no lpVerbW field in CMINVOKECOMMANDINFO. To access lpVerbW, you have to cast pici to CMINVOKECOMMANDINFOEX* first (if the cbSize is large enough). But even then, lpVerbW is only meaningful if the fMask field contains the CMIC_MASK_UNICODE flag. And only if your GetCommandString() implementation reports Unicode verbs. If you are using only menu offsets, or non-Unicode verbs, then lpVerb will suffice. However, you should use IS_INTRESOURCE() to decide whether to apply LOWORD() to lpVerb. The use of lpVerb vs lpVerbW is covered in the documentation:

      How to Implement the IContextMenu Interface: IContextMenu::InvokeCommand Method

    2. Menu items should be added by QueryContextMenu() only. This allows the Shell as well as applications to query and invoke commands without actually displaying the menu visually. IContextMenu3 was introduced to support owner-drawing of menu items when they are displayed visually.