THE CHILD DOCUMENT WINDOWS

Now let's look at HelloWndProc, which is the window procedure used for the child document windows that display ”Hello, World!“

As with any window class used for more than one window, static variables defined in the window procedure (or any function called from the window procedure) are shared by all windows created based on that window class.

Data that is unique to each window must be stored using a method other than static variables. One such technique involves window properties. Another approach (the one I used) uses memory space reserved by defining a nonzero value in the cbWndExtra field of the WNDCLASS structure used to register the window class.

In MDIDEMO, I use this space to store a local memory handle that references a block of memory the size of the HELLODATA structure. HelloWndProc allocates this memory during the WM_CREATE message, locks it, initializes the two fields (which indicate the currently checked menu item and the text color), unlocks the block, and stores the local memory handle using SetWindowWord.

When processing a WM_COMMAND message for changing the text colors (recall that these messages originate in the frame window procedure), HelloWndProc uses GetWindowWord to obtain a handle to the memory block containing the HELLODATA structure. Using this structure, HelloWndProc unchecks the checked menu item, checks the selected menu item, and saves the new color.

A document window procedure receives the WM_MDIACTIVATE message whenever the window becomes active or inactive (indicated by a TRUE or FALSE value in wParam). You'll recall that the MDIDEMO program has three different menus: MdiMenuInit for when no documents are present, MdiMenuHello for when a Hello document window is active, and MdiMenuRect for when a Rect document window is active.

The WM_MDIACTIVATE message provides an opportunity for the document window to change the menu. If wParam is TRUE (meaning the window is becoming active), HelloWndProc changes the menu to MdiMenuHello. If wParam is FALSE, HelloWndProc changes the menu to MdiMenuInit.

HelloWndProc changes the menu by sending a WM_MDISETMENU message to the client window. The client window processes this message by removing the document list from the current menu and appending it to the new menu. This is how the document list is transferred from the MdiMenuInit menu (which is in effect when the first document is created) to the MdiMenuHello menu. Do not use the SetMenu function to change a menu in an MDI application.

Another little chore involves the checkmarks on the Color submenu. Program options such as this should be unique to each document. For example, you should be able to set black text in one window and red text in another. The menu checkmarks should reflect the option chosen in the active window. For this reason, HelloWndProc unchecks the selected menu item when the window is becoming inactive and checks the appropriate item when the window is becoming active.

The window procedure gets the first WM_MDIACTIVATE message with wParam set to TRUE when the window is first created and gets the last message with wParam set to FALSE when the window is destroyed. When the user switches from one document to another, the first document window receives a WM_MDIACTIVATE message with wParam set to FALSE (at which time it sets the menu to MdiMenuInit) and the second document window receives a WM_MDIACTIVATE message with wParam set to TRUE (at which time it sets the menu to MdiMenuHello or MdiMenuRect as appropriate). If all the windows are closed, the menu is left as MdiMenuInit.

You'll recall that FrameWndProc sends the child window a WM_QUERYENDSESSION when the user selects Close or Close All from the menu. HelloWndProc processes the WM_QUERYENDSESSION and WM_CLOSE messages by displaying a message box and asking the user whether the window can be closed. (In a real program, this message box would ask whether a file needed to be saved.) If the user indicates that the window should not be closed, the window procedure returns 0.

During the WM_DESTROY message, HelloWndProc frees the local memory block allocated during the WM_CREATE message.

All unprocessed messages must be passed on to DefMDIChildProc (not DefWindowProc) for default processing. Several messages must be passed to DefMDIChildProc whether the child window procedure does something with them or not. These are: WM_CHILDACTIVATE, WM_GETMINMAXINFO, WM_MENUCHAR, WM_MOVE, WM- SETFOCUS, WM_SIZE, and WM_SYSCOMMAND.

RectWndProc is fairly similar to HelloWndProc in much of the overhead involved, but it's a little simpler (no menu options are involved and the window does not verify with the user whether it can be closed), so I needn't discuss it. But note that RectWndProc breaks after processing WM_SIZE so it is passed to DefMDIChildProc.