Building a Dynamic Menu for TrackPopupMenu

ID: Q105169


The information in this article applies to:


SUMMARY

In most cases, a menu to be used in the call to TrackPopupMenu can be created at design time using the App Studio. However, when the menu items are dynamic and not known until run time, it is necessary to build the menu from scratch using the CMenu member functions.

Due to the way the Microsoft Foundation Classes (MFC) implements the CMenu class, combined with the way Windows deletes menu resources, potential problems can occur when the CMenu object's destructor is called. Specifically, the destructor calls ::DeleteMenu() to delete the Windows menu resource. If the pop-up menu contains other pop-up menus (known as cascading menus), these menus must be detached using the CMenu::Detach() function. These menus should be detached before the top level CMenu object is destroyed. Failure to Detach() the cascading pop-up menus causes the debugging kernel of Windows to generate the error "Invalid HMENU: FatalExit code = 0x6041".


MORE INFORMATION

Suppose the pop-up menu has the following items:


   Apples
   Pears
   Grapes
   Other Misc. Fruit >
                     Mangos
                     Tomatoes 
To build this menu at run time, the following code may be used:

   CMenu MainTPMMenu;
   CMenu MiscFruitMenu;

   MainTPMMenu.CreatePopupMenu();
   MainTPMMenu.AppendMenu(MF_STRING | MF_ENABLED, 42, "Apples");
   MainTPMMenu.AppendMenu(MF_STRING | MF_ENABLED, 43, "Pears");
   MainTPMMenu.AppendMenu(MF_STRING | MF_ENABLED, 43, "Grapes");
   MiscFruitMenu.CreatePopupMenu();
   MiscFruitMenu.AppendMenu(MF_STRING | MF_ENABLED, 40, "Mangos");
   MiscFruitMenu.AppendMenu(MF_STRING | MF_ENABLED, 41, "Tomatoes");
   MainTPMMenu.AppendMenu(MF_STRING | MF_POPUP | MF_ENABLED,
                          (UINT)MiscFruitMenu.m_hMenu,
                          "Other Misc. Fruit");
   MainTPMMenu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x,
                              pt.y, this, NULL); 
On the surface, this code appears fine. However, a FatalExit can occur under the debug kernel of Windows.

When an application calls ::DeleteObject() to delete a menu resource, Windows looks at the menu and determines whether it contains any cascading pop-up menus. If so, Windows deletes the menu resource associated with the cascading pop-up menu.

Now consider the following: when a CMenu object's destructor is called, the menu handle associated with the CMenu is deleted from the GDI heap using ::DeleteObject(). This leads to the problem. In the above example, if MainTPMMenu is destroyed first, Windows will look at the hMenu and determine that it has an associated cascading pop-up menu. Windows then deletes the hMenu for this cascading pop-up menu. Now, the MiscFruitMenu object is destroyed and ::DeleteObject() is called to delete the hMenu; however, the hMenu has already been deleted by Windows. This, of course, leads to the FatalExit.

The solution to this problem is to use CMenu::Detach() to detach any cascading pop-up menus. You should place the call to Detach() somewhere in your code such that it is executed BEFORE any of the CMenu objects are destroyed. In the above example, you could simply call MiscFruitMenu.Detach() after the call to TrackPopupMenu().

Additional query words: kbinf 1.00 1.50 2.00 2.50 popup pop up


Keywords          : kb16bitonly 
Version           : 
Platform          : 
Issue type        : 

Last Reviewed: August 5, 1999