Microsoft KB Archive/67244

PRB: Using Multiple Menus in an MDI Application

PSS ID Number: Q67244 Article last modified on 04-26-1993

3.00 WINDOWS

Summary: Multiple document interface (MDI) applications with more than one class of MDI child windows commonly use a different menu to control windows of each class. This can be accomplished by selecting the appropriate class-specific menu each time an MDI child window becomes active. This article describes two common programming errors that can cause MDI applications with multiple menus to work incorrectly. The two problems manifest themselves as follows: Problem 1: Each time the MDI application terminates, the percentage of Free System Resources is lower. (The value for Free System Resources is displayed in the About Program Manager dialog box.) Problem 2: Creating a new child window in a maximized state causes the menu bar to appear strangely and/or creates an Unrecoverable Application Error (UAE). Charles Petzold’s MDIDEMO sample application on pages 57-59 and 63 in the July 1990 issue of the “Microsoft Systems Journal” (Vol. 5, No. 4) exhibits the first problem. If MDIDEMO is altered to create child windows in a maximized state, it will exhibit the second problem. The MDIDEMO application can be found in the Software/Data Library by searching on the keyword MDI, the Q number of this article, or S12629. MDIDEMO was archived using the PKware file-compression utility. Below are descriptions of the solutions to these two problems.

=
========================================================== Problem 1: Each time the MDI application terminates, the percentage of Free System Resources is lower. =======================================================================

Solution
This problem can occur if menus used by the MDI application are not destroyed before the application terminates. Menus that are used by a program take up space in the shared segment that holds the USER heap. When the program terminates, Windows will reclaim the memory used by the program’s current menu. If there are other menus that are not currently in use, that memory must be reclaimed by calling DestroyMenu before the application terminates.

Example
MDIDEMO uses LoadMenu to load three menus: hMenuInit, hMenuHello, and hMenuRect. When MDIDEMO exits, the memory allocated for two of these menus is not reclaimed; about 1K is lost in the USER heap, and Free System Resources go down. Adding this code to the FrameWndProc function in MDIDEMO will remove this problem: case WM_DESTROY: { HMENU hCurrentMenu = GetMenu(hwnd); if (hMenuInit != hCurrentMenu) DestroyMenu(hMenuInit); if (hMenuHello != hCurrentMenu) DestroyMenu(hMenuHello); if (hMenuRect != hCurrentMenu) DestroyMenu(hMenuRect); PostQuitMessage(0); return 0; } Note that DestroyMenu must NOT be called on the menu that is currently in use, or the program will crash.

=
========================================================== Problem 2: Creating a new child window in a maximized state causes the menu bar to appear strangely and/or creates an Unrecoverable Application Error (UAE). =======================================================================

Solution
This problem occurs when an MDI child window’s window procedure sends a WM_MDISETMENU message to the MDI client window. This usually occurs while the child window is processing the WM_MDIACTIVATE message, when the child is changing the application’s current menu to the menu appropriate for that child. During the processing of the WM_MDIACTIVATE message, Windows modifies its internal structures. Changing the menu at this time is very dangerous. The accepted method is to have the window procedure for the MDI frame window send the WM_MDISETMENU message to the MDI client window. Therefore, when an MDI child is activated, and it is necessary to change the current menu, the child should call PostMessage to post a user-defined message to the frame window. When the frame window receives this message, the frame sends a WM_MDISETMENU message to the MDI client window. The frame then redraws the menu bar by calling DrawMenuBar.

Example
To implement the above approach in the MDIDEMO program, perform these three steps: 1. Create a user-defined message. For example: #define IDM_MENUCHANGE WM_USER + 1000 2. When the child window receives a WM_MDIACTIVATE message, instead of sending the MDI client window a WM_MDISETMENU message, use PostMessage to post the new message to the MDI frame window, as follows: PostMessage(hwndFrame, WM_COMMNAND, IDM_MENUCHANGE, MAKELONG(hMenuRect, hMenuRectWindow)); 3. In FrameWndProc, send the WM_MDISETMENU message to the MDI client window in response to the new message. Add this code to the switch statement in processing of the WM_COMMAND message: case IDM_MENUCHANGE: SendMessage(hwndClient, WM_MDISETMENU, 0, lParam); DrawMenuBar(hwnd); return 0; Note that DrawMenuBar must be called to avoid some redrawing problems. More Information: For more information on the Windows 3.0 multiple document interface (MDI), query on the following words in the Microsoft Knowledge Base: prod(winsdk) and MDI

Additional reference words: 3.00 3.0 MSJ V5-4 softlib MDIDEMO.ZIP KBCategory: KBSubcategory: UsrMdiMenus

Copyright Microsoft Corporation 1993.