How do the menu functions find items?

Date:March 19, 2007 / year-entry #98
Orig Link:
Comments:    7
Summary:Most of the menu item functions such as GetMenuItemInfo allow you specify the menu item either by position or by command. Some of them use the MF_BYPOSITION and MF_BYCOMMAND flags. Others separate the search algorithm into a separate fByPosition flag. Searching for menu items by position is straightforward: The specified position is used as a...

Most of the menu item functions such as GetMenuItemInfo allow you specify the menu item either by position or by command. Some of them use the MF_BYPOSITION and MF_BYCOMMAND flags. Others separate the search algorithm into a separate fByPosition flag.

Searching for menu items by position is straightforward: The specified position is used as a zero-based index into the menu. In other words, the first item in the menu is item zero.

Searching for menu items by command is trickier. The menu manager searches the entire menu hierarchy, including submenus, for an item with the command you specify. If more than one menu item has the identifier you requested, then one of them is chosen arbitrarily. Searching the hierarchy for a command means that you can, for example, remove or disable a menu item by just passing the root menu (which you typically have easy access to) and the item identifier. If the submenus were not searched, then synchronizing menu states would be a much more cumbersome affair.

But what if your menu has multiple items with the same identifier? Well, the short answer is, "Then don't use MF_BYCOMMAND." You can still use MF_BYPOSITION to access your menu items. But why would you have multiple items with the same identifier in the first place? When the user selects the menu, a WM_COMMAND is posted to the window with the menu identifier as one of its parameters, and none of the other parameters gives you the menu handle. If you have multiple menu items with the same identifier, you won't be able to tell which of them the user picked!

There was an emotional discussion a while back that generated far more heat than light. But I can use my psychic powers to explain what that person was seeing, even though not enough information was provided in the original problem description, and it's not a bug in Windows.

Addressing the original complaint: If you have a menu with more than one item with the same identifier, then MF_BYCOMMAND is ambiguous, and all that is promised is that some item with that identifier will be found. It might not be the one you wanted, but since you gave multiple items the same name, the menu manager did the best it could. This is analogous to other searching functions like FindWindow and GetDlgItem, which operate on the first item they find. If multiple items match the criteria you specify, it just returns one of them.

As for the specific problem: It so happens that there is no cache, at least not yet. (But who knows, there might be a cache in the future if we discover that lots of applications query for the same item in rapid succession.) But what this person didn't realize is that the unnamed custom GUI library they're using doesn't create submenus until the WM_INITMENUPOPUP message is received. The reason why the item isn't found before the submenu is opened is that until the submenu is opened, the submenu doesn't exist. And naturally, you can't find something that doesn't exist.

Comments (7)
  1. BryanK says:

    One possible case for having a menu with multiple items with the same identifier is when all of those menu items do the same thing (e.g. the same setting in multiple places).  For instance, if you have both an Edit->Preferences menu and a Tools->Options menu, they might both have the same identifier, because they both bring up the same program-options dialog.

    Now the only reason to do something like this is probably to keep a UI the same across multiple different hosts, or something like that (e.g. Windows says "use Tools->Options", but Java, or some other setup, might say "use Edit->Preferences").  The easiest way to do this (and keep your number of code paths low) is probably to use both menu items on both hosts.

    Now in that case, you don’t care that you can’t find out which menu item the user chose when you get the WM_COMMAND message, but you don’t care, either.  (What would be a problem is disabling both menu items using MF_BYCOMMMAND; it sounds like the API would disable only one or the other.)

    I don’t suppose menu items have a "class", do they?  (Like CSS’s class versus ID distinction: multiple items in HTML can have the same class, but only one can have a given ID.)  That would make both situations work.

  2. John says:

    I understand what you are saying, but it still doesn’t make sense to me.  Let’s see some code.

    HMENU hMenu; // has an item with id = 12345

    HMENU hSubMenu; // non-existant; will have an item with id = 12345

    GetMenuItemInfo(hMenu, 12345, FALSE, …);

    GetMenuItemInfo(hSubMenu, 12345, FALSE, …);

    From what I can tell the guy is claiming that the result of the above two function calls is identical, returning the item in hMenu both times.

    I guess my question is this: what is the value of hSubMenu when the guy was trying to get its subitem?  If hSubMenu is NULL, shouldn’t GetMenuItemInfo() fail?  If hSubMenu is a handle to an empty submenu, shouldn’t GetMenuItemInfo() fail?  If hSubMenu is not a valid menu handle, shouldn’t the result be undefined?

  3. bramster says:

    Im am reminded of a situation in the dark ages of the late 1980s, where a "easy-to-use" data management program provided an export function.  Unfortunately, when exporting to a .dbf file, it took no pains to ensure that the first 10 characters of a field were unique.

    The result was a dbf file for dbaseIV or foxpro with duplicated field names, and as such, unreadable by dbaseIV or Foxpro.

    It lead me to write something which would analyze the header, and convert the .dbf to a flat file.  Then I could recreate a valid data structure.

  4. John says:

    Ok, I misunderstood the original complaint.  I thought he was passing hSubMenu to the fuction and getting an item in hMenu when he was really passing hMenu to the fuction and getting an item in hSubMenu.  hSubMenu gets created on WM_INITMENUPOPUP, and after that the new item in the new submenu is returned because (presumably) it is encountered first in the menu tree.  My mistake.

  5. Nick says:

    By Your Command!

    (Sorry, couldn’t resist.)

  6. Dustin Long says:

    You seem to have a lot of posts responding to programmer mistakes on various forums and groups. How do you find all these? Are they sent along as tips or do you go looking for them? I think it’s a really cool service to perform, but if you just look for them and post about it here, it’s a shame that the OP probably will never find your explanation.

    [I use the postings as springboards for discussion. Solving specific problems is not the goal. -Raymond]
  7. Leo Davidson says:

    I like the usenet post’s subject:

    "MAJOR BUG in Windows Menus!!!"

    One could probably create a fairly accurate formula to describe the relationship between the number of capital letters, exclamation marks and assertions of things like "major bugs" in a message subject and the likelihood that the problem is between the screen and the chair.



    I sailed for days trying to find the edge of the Earth but ended up back where I started from.


    That said, I can sympathise that the poster may have begun to wonder if something might be wrong in Windows, if he didn’t realise that sub-menus were searched and also didn’t realise that the sub-menus were only created on first use by the 3rd party framework. We all have those programming moments where we wonder if it’s us doing something wrong or if we’ve discovered a bug. The important thing is to dig deeper and find proof either way, and if you can’t find proof or don’t know how to dig then you ask other people, but without shouting about how you’ve found the world’s worst and most blatant bug. 9 times out of 10 it is something you are doing wrong, not Windows or whatever else.

    (Exception: When dealing with the Adobe Reader ActiveX control, if something crashes or behaves strangely then it almost always *isn’t* your fault.)

Comments are closed.

*DISCLAIMER: I DO NOT OWN THIS CONTENT. If you are the owner and would like it removed, please contact me. The content herein is an archived reproduction of entries from Raymond Chen's "Old New Thing" Blog (most recent link is here). It may have slight formatting modifications for consistency and to improve readability.

WHY DID I DUPLICATE THIS CONTENT HERE? Let me first say this site has never had anything to sell and has never shown ads of any kind. I have nothing monetarily to gain by duplicating content here. Because I had made my own local copy of this content throughout the years, for ease of using tools like grep, I decided to put it online after I discovered some of the original content previously and publicly available, had disappeared approximately early to mid 2019. At the same time, I present the content in an easily accessible theme-agnostic way.

The information provided by Raymond's blog is, for all practical purposes, more authoritative on Windows Development than Microsoft's own MSDN documentation and should be considered supplemental reading to that documentation. The wealth of missing details provided by this blog that Microsoft could not or did not document about Windows over the years is vital enough, many would agree an online "backup" of these details is a necessary endeavor. Specifics include:

<-- Back to Old New Thing Archive Index