windowswinapimenu

WINAPI, remove popup menu from menu


I would like to have dynamic menu in my application. This dynamic menu should contain popupmenus which will be added and removed on the fly. For the first approach I made dynamic menu created with single menu items.

AppendMenu(menu, MF_STRING, item_id, "TEST");

I have created algorithm which generate item_id and store them in array, so I could remove them by

DeleteMenu(menu, id_to_be_deleted, MF_BYCOMMAND);

I do not see any pitfalls of this implementation and I am happy with it. But then stuck with final implementation. I would like my menus would be popup menus.

new_popup_menu = CreatePopupMenu();
AppendMenu(new_popup_menu, MF_STRING, 1, "TEST1");
AppendMenu(new_popup_menu, MF_STRING, 2, "TEST2");
AppendMenu(new_popup_menu, MF_STRING, 3, "TEST3");
AppendMenu(menu, MF_STRING|MF_POPUP, 
          (UINT_PTR)new_popup_menu, "dynamic menu");

This code works as expected, but I have no idea how to remove "new_popup_menu" from "menu" since the "UINT_PTR uIDNewItem" parameter of AppendMenu now is used as handle to submenu, not ID and cannot be used with DeleteMenu+MF_BYCOMMAND. Is there any way to remove this submenu item other then DeleteMenu+MF_BYPOSITION? Is there a way to get menu item position by handle which is returned by CreatePopupMenu())? I feel implementation algorithm of tracking which menu is on which position is pain in the ass. Since Windows has API to insert the menu after other specific menu, recreating whole menu tree is a waste of CPU time.


Solution

  • If you want to create a menu item that opens a submenu and has an ID then create it with InsertMenuItem(...) rather than AppendMenu(...).

    InsertMenuItem(...) lets you fill in a struct that specifies all of the properties you want to be set on the menu item you are creating, including ID and submenu. A lot of Win32 works this way: AppendMenu(...) is like a shorthand version for the more verbose version of the same function. When you run into situations in which you can't do something reasonable with a certain Win32 call, look for a synonymous call that takes a *INFO structure.

    Code below:

    ...
    HMENU menu_bar = GetMenu(hWnd);
    HMENU new_menu = CreateMenu();
    AppendMenu(menu_bar, MF_POPUP, (UINT_PTR)new_menu, "foobar");
    AppendMenu(new_menu, MF_ENABLED | MF_STRING, 1002, "item1");
    AppendMenu(new_menu, MF_ENABLED | MF_STRING, 1003, "item2");
    
    HMENU dynamic_popup = CreatePopupMenu();
    AppendMenu(dynamic_popup, MF_ENABLED | MF_STRING, 1004, "mumble");
    AppendMenu(dynamic_popup, MF_ENABLED | MF_STRING, 1005, "quux");
    
    // Below will add an item named "dynamic menu" to the end of new_menu
    // that has an ID of 1006.
    
    MENUITEMINFO mii = { 0 };
    mii.cbSize = sizeof(mii);
    mii.fMask = MIIM_SUBMENU | MIIM_STRING | MIIM_ID;
    mii.dwTypeData = (LPSTR)"dynamic menu";
    mii.hSubMenu = dynamic_popup;
    mii.wID = 1006;
    InsertMenuItem(new_menu, 0, FALSE, &mii);
    
    //DeleteMenu(new_menu, 1006, MF_BYCOMMAND);
    ...