How to find the menu item (if any) that this HMENU opens upon activation?

I would like to implement a function with a prototype

/* Locates the menu item of the application which caused the given menu 'mnu' to
 * show up.
 * @return true if the given menu 'mnu' was opened by another menu item, false
 * if not.
 */
bool getParentMenuItem( HMENU mnu, HMENU *parentMenu, int *parentMenuIdx );

Given the HMENU descriptor, I would like to know which menu item (if any) in the application opened it. This is basically the inverse of GetSubMenu .

My current approach is to look into each HMENU of the top-level windows of the application and check if I can find a menu item that will open this submenu upon activation. I do this recursively using GetMenuItemCount / GetSubMenu .

This is pretty inefficient though, and it fails for a menu that opens with context menu items. Therefore, I am wondering:

- , ( ), HMENU ?

: , ; ( SetWindowsHookEx), , , . , , ( (HMENU, int)) HMENU, . getParentMenuItem .

: , , , , , , β†’ , .

, , (); ?

+3
2

MENUINFO.dwMenuData , :

MENUINFO mi;
mi.cbSize = sizeof(MENUINFO);
mi.dwMenuData = (ULONG_PTR)<parent HMENU if this is a sub menu>
mi.fMask = MIM_MENUDATA;

SetMenuInfo(hCreatedMenu, &mi);

dwMenuData :

bool getParentMenuItem(HMENU mnu, HMENU *parentMenu, int *parentMenuIdx)
{
    MENUINFO mi;
    mi.cbSize = sizeof(MENUINFO);
    mi.fMask = MIM_MENUDATA;

    if (!GetMenuInfo(mnu,&mi) || mi.dwMenuData == 0)
        return false;

    *parentMenu = (HMENU)mi.dwMenuData;

    // not sure how or why you need the parentMenuIdx, but you should be able
    // to derive that from the parent HMENU

    return true;
}

: , , WH_CALLWNDPROC , . ( ) , - HMENU .

+1

​​ . , .rc, , , . ( , , , ).

, , .

, POPUP, POPUP, . .

. EnableSubmenuItem EnableMenuItem ID. . , . , - , .

(, , hMenu , FindParentMenu ).

////////////////////////////////////////////////////////////////////////////////
// MenuContainsID - return TRUE if menu hMenu contains an item with specified ID
////////////////////////////////////////////////////////////////////////////////

static BOOL MenuContainsID (HMENU hMenu, UINT id)
{
    int pos;                                          // use signed int so we can count down and detect passing 0
    MENUITEMINFO mf;

    ZeroMemory(&mf, sizeof(mf));                      // request just item ID
    mf.cbSize = sizeof(mf);
    mf.fMask = MIIM_ID;

    for (pos = GetMenuItemCount(hMenu); --pos >= 0; )         // enumerate menu items
        if (GetMenuItemInfo(hMenu, (UINT) pos, TRUE, &mf))    // if we find the ID we are looking for return TRUE
            if (mf.wID == id)
                return TRUE;

    return FALSE;
}

////////////////////////////////////////////////////////////////////////////////
// MenuItemIsSubmenu - returns TRUE if item # pos (position) of menu hMenu is a
// submenu. Sets phSubMenu to menu handle if so
////////////////////////////////////////////////////////////////////////////////

static BOOL MenuItemIsSubmenu (HMENU hMenu, UINT pos, HMENU *phSubMenu)
{
    MENUITEMINFO mf;

    ZeroMemory(&mf, sizeof(mf));                      // request just submenu handle
    mf.cbSize = sizeof(mf);
    mf.fMask = MIIM_SUBMENU;

    if (! GetMenuItemInfo(hMenu, pos, TRUE, &mf))     // failed to get item?
        return FALSE;

    *phSubMenu = mf.hSubMenu;                         // pass back by side effect
    return (mf.hSubMenu != NULL);                     // it a submenu if handle is not NULL
}

////////////////////////////////////////////////////////////////////////////////
// MenuItemIsEnabled - returns true if item # pos (position) of menu hMenu is
// enabled (that is, is not disabled or grayed)
////////////////////////////////////////////////////////////////////////////////

static BOOL MenuItemIsEnabled (HMENU hMenu, UINT pos)
{
    return ! (GetMenuState(hMenu, pos, MF_BYPOSITION) & (MF_GRAYED | MF_DISABLED));
}

////////////////////////////////////////////////////////////////////////////////
// FindParentMenu - returns handle of the submenu of menu bar hMenu that contains
// an item with id "id". Position of this submenu is passed by side effect to
// pParentPos, and /its/ parent menu is passed by side effect through phGrandparentMenu.
////////////////////////////////////////////////////////////////////////////////

static HMENU FindParentMenu (HMENU hMenu, UINT id, HMENU *phGrandparentMenu, UINT *pparentPos)
{
    int pos;                                          // use signed int so we can count down and detect passing 0
    HMENU hSubMenu, hx;

    for (pos = GetMenuItemCount(hMenu); --pos >= 0; ) {
        if (MenuItemIsSubmenu(hMenu, (UINT) pos, &hSubMenu)) {
            if (MenuContainsID(hSubMenu, id)) {
                *phGrandparentMenu = hMenu;           // set grandparent info by side effect
                *pparentPos = (UINT) pos;
                return hSubMenu;                      // we found the item directly
            }

            if ((hx = FindParentMenu(hSubMenu, id, phGrandparentMenu, pparentPos)) != NULL)
                return hx;                            // we found the item recursively (in a sub-sub menu). It set grandparent info
        }
    }

    return NULL;
}

////////////////////////////////////////////////////////////////////////////////
// AllSubitemsAreDisabled - returns TRUE if all items in a submenu are disabled
////////////////////////////////////////////////////////////////////////////////

static BOOL AllSubitemsAreDisabled (HMENU hMenu)
{
    int pos;                                          // use signed int so we can count down and detect passing 0

    for (pos = GetMenuItemCount(hMenu); --pos >= 0; )
        if (MenuItemIsEnabled(hMenu, (UINT) pos))
            return FALSE;                             // finding one enabled item is enough to stop

    return TRUE;
}

////////////////////////////////////////////////////////////////////////////////
// EnableSubMenuItem - like EnableMenuItem, enables or disables a menu item
// by ID (only; not position!) where hMenu is top level menu.
// Added bonus: If the item is in a pop-up menu, and all items in the popup are
// now disabled, we disable the popup menu itself.
//
// Example:
//        EnableSubMenuItem(hMainMenu, IDM_CONFIGURE, MF_GRAYED);
//
////////////////////////////////////////////////////////////////////////////////

void EnableSubMenuItem (HMENU hMenu, UINT id, UINT enable)
{
    HMENU hParentMenu, hGrandparentMenu;
    UINT parentPos;

    // EnableMenuItem does its job recursively and takes care of the item
    EnableMenuItem(hMenu, id, enable | MF_BYPOSITION);

    // But popup menus don't have IDs so we have find the parent popup menu, and its parent (the
    // grandparent menu), so we can enable or disable the popup entry by position

    if ((hParentMenu = FindParentMenu(hMenu, id, &hGrandparentMenu, &parentPos)) != NULL) 
        EnableMenuItem(hGrandparentMenu, parentPos,
           MF_BYPOSITION | (AllSubitemsAreDisabled(hParentMenu) ? MF_GRAYED : MF_ENABLED));
}
+1

Source: https://habr.com/ru/post/1715867/


All Articles