Frerich Raabe Frerich Raabe - 8 months ago 31
C Question

How to find the menu item (if any) which opens a given HMENU when activated?

I'd like to implement a function with the 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 a HMENU handle, I'd like to be able to find out which menu item (if any) in the application opened it. This is basically the reverse of the GetSubMenu function.

My current approach is to look into each HMENU of the top level windows of the application and check for whether I can find a menu item which would open the given sub menu when activated. I do this recursively, using GetMenuItemCount/GetSubMenu.

This is rather inefficient though, and it fails for menus which are opened by context menu items. Hence, I'm wondering:

Does anybody have a nice idea how to find the menu item (if any) which opens a given HMENU when activated?

UPDATE: An idea which just came to my mind; it should be possible (using the SetWindowsHookEx function) to install a hook which gets notified of all input events which happened in a menu. Whenever a menu item activation is detected, memorize the menu item (identified by a (HMENU,int) pair) and the HMENU which will get opened by the menu item in a global map. The
function above could then simply perform a lookup into the map.

UPDATE to the update: The hooking idea described in the update above won't work as it is since it will of course only recognize menu item -> menu associations for items which have been activated at some point.

This feels a bit ugly though since it reqiures me to keep a lot of state (the map); are there any easier possibilities?


You could try setting MENUINFO.dwMenuData to the parent menu handle for all menus you create in your application:

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

SetMenuInfo(hCreatedMenu, &mi);

Then you only need to query this dwMenuData field in your function:

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;

Edit: If you don't have control over how all menus are created, you could use a WH_CALLWNDPROC hook to trap when a menu is first created. A good article (with source code) describes how this can be done - you could then look at trying to inject the parent HMENU into the created menu using the method described above.