THE SAMPLE PROGRAM

The Windows 3 Software Development Kit (SDK) includes a sample program called MULTIPAD that demonstrates how to write an MDI program. However, MULTIPAD contains quite a bit of code that has nothing to do with MDI. It might be easier for you to get a better feel for MDI programming by examining a smaller program that does little except demonstrate the MDI features.

The components of this program, called MDIDEMO, are shown in Figure 18-2.

MDIDEMO.MAK

#-----------------------

# MDIDEMO.MAK make file

#-----------------------

mdidemo.exe : mdidemo.obj mdidemo.def mdidemo.res

link mdidemo, /align:16, NUL, /nod slibcew libw, mdidemo

rc mdidemo.res

mdidemo.obj : mdidemo.c mdidemo.h

cl -c -Gsw -Ow -W2 -Zp mdidemo.c

mdidemo.res : mdidemo.rc mdidemo.h

rc -r mdidemo.rc

MDIDEMO.C

/*--------------------------------------------------------

MDIDEMO.C -- Multiple Document Interface Demonstration

(c) Charles Petzold, 1990

--------------------------------------------------------*/

#include <windows.h>

#include <stdlib.h>

#include "mdidemo.h"

long FAR PASCAL FrameWndProc (HWND, WORD, WORD, LONG) ;

BOOL FAR PASCAL CloseEnumProc (HWND, LONG) ;

long FAR PASCAL HelloWndProc (HWND, WORD, WORD, LONG) ;

long FAR PASCAL RectWndProc (HWND, WORD, WORD, LONG) ;

// structure for storing data unique to each Hello child window

typedef struct

{

short nColor ;

COLORREF clrText ;

}

HELLODATA ;

typedef HELLODATA NEAR *NPHELLODATA ;

// structure for storing data unique to each Rect child window

typedef struct

{

short cxClient ;

short cyClient ;

}

RECTDATA ;

typedef RECTDATA NEAR *NPRECTDATA ;

// global variables

char szFrameClass [] = "MdiFrame" ;

char szHelloClass [] = "MdiHelloChild" ;

char szRectClass [] = "MdiRectChild" ;

HANDLE hInst ;

HMENU hMenuInit, hMenuHello, hMenuRect ;

HMENU hMenuInitWindow, hMenuHelloWindow, hMenuRectWindow ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,

LPSTR lpszCmdLine, int nCmdShow)

{

HANDLE hAccel ;

HWND hwndFrame, hwndClient ;

MSG msg ;

WNDCLASS wndclass ;

hInst = hInstance ;

if (!hPrevInstance)

{

// Register the frame window class

wndclass.style = CS_HREDRAW | CS_VREDRAW ;

wndclass.lpfnWndProc = FrameWndProc ;

wndclass.cbClsExtra = 0 ;

wndclass.cbWndExtra = 0 ;

wndclass.hInstance = hInstance ;

wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;

wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;

wndclass.hbrBackground = COLOR_APPWORKSPACE + 1 ;

wndclass.lpszMenuName = NULL ;

wndclass.lpszClassName = szFrameClass ;

RegisterClass (&wndclass) ;

// Register the Hello child window class

wndclass.style = CS_HREDRAW | CS_VREDRAW ;

wndclass.lpfnWndProc = HelloWndProc ;

wndclass.cbClsExtra = 0 ;

wndclass.cbWndExtra = sizeof (LOCALHANDLE) ;

wndclass.hInstance = hInstance ;

wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;

wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;

wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;

wndclass.lpszMenuName = NULL ;

wndclass.lpszClassName = szHelloClass ;

RegisterClass (&wndclass) ;

// Register the Rect child window class

wndclass.style = CS_HREDRAW | CS_VREDRAW ;

wndclass.lpfnWndProc = RectWndProc ;

wndclass.cbClsExtra = 0 ;

wndclass.cbWndExtra = sizeof (LOCALHANDLE) ;

wndclass.hInstance = hInstance ;

wndclass.hIcon = NULL ;

wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;

wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;

wndclass.lpszMenuName = NULL ;

wndclass.lpszClassName = szRectClass ;

RegisterClass (&wndclass) ;

}

// Obtain handles to three possible menus and submenus

hMenuInit = LoadMenu (hInst, "MdiMenuInit") ;

hMenuHello = LoadMenu (hInst, "MdiMenuHello") ;

hMenuRect = LoadMenu (hInst, "MdiMenuRect") ;

hMenuInitWindow = GetSubMenu (hMenuInit, INIT_MENU_POS) ;

hMenuHelloWindow = GetSubMenu (hMenuHello, HELLO_MENU_POS) ;

hMenuRectWindow = GetSubMenu (hMenuRect, RECT_MENU_POS) ;

// Load accelerator table

hAccel = LoadAccelerators (hInst, "MdiAccel") ;

// Create the frame window

hwndFrame = CreateWindow (szFrameClass, "MDI Demonstration",

WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,

CW_USEDEFAULT, CW_USEDEFAULT,

CW_USEDEFAULT, CW_USEDEFAULT,

NULL, hMenuInit, hInstance, NULL) ;

hwndClient = GetWindow (hwndFrame, GW_CHILD) ;

ShowWindow (hwndFrame, nCmdShow) ;

UpdateWindow (hwndFrame) ;

// Enter the modified message loop

while (GetMessage (&msg, NULL, 0, 0))

{

if (!TranslateMDISysAccel (hwndClient, &msg) &&

!TranslateAccelerator (hwndFrame, hAccel, &msg))

{

TranslateMessage (&msg) ;

DispatchMessage (&msg) ;

}

}

return msg.wParam ;

}

long FAR PASCAL FrameWndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)

{

static HWND hwndClient ;

CLIENTCREATESTRUCT clientcreate ;

FARPROC lpfnEnum ;

HWND hwndChild, hwndNext ;

MDICREATESTRUCT mdicreate ;

switch (message)

{

case WM_CREATE : // Create the client window

clientcreate.hWindowMenu = hMenuInitWindow ;

clientcreate.idFirstChild = IDM_FIRSTCHILD ;

hwndClient = CreateWindow ("MDICLIENT", NULL,

WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE,

0, 0, 0, 0, hwnd, 1, hInst,

(LPSTR) &clientcreate) ;

return 0 ;

case WM_COMMAND :

switch (wParam)

{

case IDM_NEWHELLO : // Create a Hello child window

mdicreate.szClass = szHelloClass ;

mdicreate.szTitle = "Hello" ;

mdicreate.hOwner = hInst ;

mdicreate.x = CW_USEDEFAULT ;

mdicreate.y = CW_USEDEFAULT ;

mdicreate.cx = CW_USEDEFAULT ;

mdicreate.cy = CW_USEDEFAULT ;

mdicreate.style = 0 ;

mdicreate.lParam = NULL ;

hwndChild = SendMessage (hwndClient, WM_MDICREATE, 0,

(LONG) (LPMDICREATESTRUCT) &mdicreate) ;

return 0 ;

case IDM_NEWRECT : // Create a Rect child window

mdicreate.szClass = szRectClass ;

mdicreate.szTitle = "Rectangles" ;

mdicreate.hOwner = hInst ;

mdicreate.x = CW_USEDEFAULT ;

mdicreate.y = CW_USEDEFAULT ;

mdicreate.cx = CW_USEDEFAULT ;

mdicreate.cy = CW_USEDEFAULT ;

mdicreate.style = 0 ;

mdicreate.lParam = NULL ;

hwndChild = SendMessage (hwndClient, WM_MDICREATE, 0,

(LONG) (LPMDICREATESTRUCT) &mdicreate) ;

return 0 ;

case IDM_CLOSE : // Close the active window

hwndChild = LOWORD (SendMessage (hwndClient,

WM_MDIGETACTIVE, 0, 0L)) ;

if (SendMessage (hwndChild, WM_QUERYENDSESSION, 0, 0L))

SendMessage (hwndClient, WM_MDIDESTROY,

hwndChild, 0L) ;

return 0 ;

case IDM_EXIT : // Exit the program

SendMessage (hwnd, WM_CLOSE, 0, 0L) ;

return 0 ;

// Messages for arranging windows

case IDM_TILE :

SendMessage (hwndClient, WM_MDITILE, 0, 0L) ;

return 0 ;

case IDM_CASCADE :

SendMessage (hwndClient, WM_MDICASCADE, 0, 0L) ;

return 0 ;

case IDM_ARRANGE :

SendMessage (hwndClient, WM_MDIICONARRANGE, 0, 0L) ;

return 0 ;

case IDM_CLOSEALL : // Attempt to close all children

lpfnEnum = MakeProcInstance (CloseEnumProc, hInst) ;

EnumChildWindows (hwndClient, lpfnEnum, 0L) ;

FreeProcInstance (lpfnEnum) ;

return 0 ;

default : // Pass to active child

hwndChild = LOWORD (SendMessage (hwndClient,

WM_MDIGETACTIVE, 0, 0L)) ;

if (IsWindow (hwndChild))

SendMessage (hwndChild, WM_COMMAND,

wParam, lParam) ;

break ; // and then to DefFrameProc

}

break ;

case WM_QUERYENDSESSION :

case WM_CLOSE : // Attempt to close all children

SendMessage (hwnd, WM_COMMAND, IDM_CLOSEALL, 0L) ;

if (NULL != GetWindow (hwndClient, GW_CHILD))

return 0 ;

break ; // i.e., call DefFrameProc ;

case WM_DESTROY :

PostQuitMessage (0) ;

return 0 ;

}

// Pass unprocessed messages to DefFrameProc (not DefWindowProc)

return DefFrameProc (hwnd, hwndClient, message, wParam, lParam) ;

}

BOOL FAR PASCAL CloseEnumProc (HWND hwnd, LONG lParam)

{

if (GetWindow (hwnd, GW_OWNER)) // check for icon title

return 1 ;

SendMessage (GetParent (hwnd), WM_MDIRESTORE, hwnd, 0L) ;

if (!SendMessage (hwnd, WM_QUERYENDSESSION, 0, 0L))

return 1 ;

SendMessage (GetParent (hwnd), WM_MDIDESTROY, hwnd, 0L) ;

return 1 ;

}

long FAR PASCAL HelloWndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)

{

static COLORREF clrTextArray [] = { RGB (0, 0, 0), RGB (255, 0, 0),

RGB (0, 255, 0), RGB ( 0, 0, 255),

RGB (255, 255, 255) } ;

static HWND hwndClient, hwndFrame ;

HDC hdc ;

HMENU hMenu ;

LOCALHANDLE hHelloData ;

NPHELLODATA npHelloData ;

PAINTSTRUCT ps ;

RECT rect ;

switch (message)

{

case WM_CREATE :

// Allocate memory for window private data

hHelloData = LocalAlloc (LMEM_MOVEABLE | LMEM_ZEROINIT,

sizeof (HELLODATA)) ;

npHelloData = (NPHELLODATA) LocalLock (hHelloData) ;

npHelloData->nColor = IDM_BLACK ;

npHelloData->clrText = RGB (0, 0, 0) ;

LocalUnlock (hHelloData) ;

SetWindowWord (hwnd, 0, hHelloData) ;

// Save some window handles

hwndClient = GetParent (hwnd) ;

hwndFrame = GetParent (hwndClient) ;

return 0 ;

case WM_COMMAND :

switch (wParam)

{

case IDM_BLACK :

case IDM_RED :

case IDM_GREEN :

case IDM_BLUE :

case IDM_WHITE :

// Change the text color

hHelloData = GetWindowWord (hwnd, 0) ;

npHelloData = (NPHELLODATA) LocalLock (hHelloData) ;

hMenu = GetMenu (hwndFrame) ;

CheckMenuItem (hMenu, npHelloData->nColor,

MF_UNCHECKED) ;

npHelloData->nColor = wParam ;

CheckMenuItem (hMenu, npHelloData->nColor,

MF_CHECKED) ;

npHelloData->clrText =

clrTextArray [wParam - IDM_BLACK] ;

LocalUnlock (hHelloData) ;

InvalidateRect (hwnd, NULL, FALSE) ;

}

return 0 ;

case WM_PAINT :

// Paint the window

hdc = BeginPaint (hwnd, &ps) ;

hHelloData = GetWindowWord (hwnd, 0) ;

npHelloData = (NPHELLODATA) LocalLock (hHelloData) ;

SetTextColor (hdc, npHelloData->clrText) ;

LocalUnlock (hHelloData) ;

GetClientRect (hwnd, &rect) ;

DrawText (hdc, "Hello, World!", -1, &rect,

DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;

EndPaint (hwnd, &ps) ;

return 0 ;

case WM_MDIACTIVATE :

// Set the Hello menu if gaining focus

if (wParam == TRUE)

SendMessage (hwndClient, WM_MDISETMENU, 0,

MAKELONG (hMenuHello, hMenuHelloWindow)) ;

// check or uncheck menu item

hHelloData = GetWindowWord (hwnd, 0) ;

npHelloData = (NPHELLODATA) LocalLock (hHelloData) ;

CheckMenuItem (hMenuHello, npHelloData->nColor,

wParam ? MF_CHECKED : MF_UNCHECKED) ;

LocalUnlock (hHelloData) ;

// Set the Init menu if losing focus

if (wParam == FALSE)

SendMessage (hwndClient, WM_MDISETMENU, 0,

MAKELONG (hMenuInit, hMenuInitWindow)) ;

DrawMenuBar (hwndFrame) ;

return 0 ;

case WM_QUERYENDSESSION :

case WM_CLOSE :

if (IDOK != MessageBox (hwnd, "OK to close window?", "Hello",

MB_ICONQUESTION | MB_OKCANCEL))

return 0 ;

break ; // i.e., call DefMDIChildProc

case WM_DESTROY :

hHelloData = GetWindowWord (hwnd, 0) ;

LocalFree (hHelloData) ;

return 0 ;

}

// Pass unprocessed message to DefMDIChildProc

return DefMDIChildProc (hwnd, message, wParam, lParam) ;

}

long FAR PASCAL RectWndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)

{

static HWND hwndClient, hwndFrame ;

HPEN hBrush ;

HDC hdc ;

LOCALHANDLE hRectData ;

NPRECTDATA npRectData ;

PAINTSTRUCT ps ;

short xLeft, xRight, yTop, yBottom, nRed, nGreen, nBlue ;

switch (message)

{

case WM_CREATE :

// Allocate memory for window private data

hRectData = LocalAlloc (LMEM_MOVEABLE | LMEM_ZEROINIT,

sizeof (RECTDATA)) ;

SetWindowWord (hwnd, 0, hRectData) ;

// Start the timer going

SetTimer (hwnd, 1, 250, NULL) ;

// Save some window handles

hwndClient = GetParent (hwnd) ;

hwndFrame = GetParent (hwndClient) ;

return 0 ;

case WM_SIZE : // Save the window size

hRectData = GetWindowWord (hwnd, 0) ;

npRectData = (NPRECTDATA) LocalLock (hRectData) ;

npRectData->cxClient = LOWORD (lParam) ;

npRectData->cyClient = HIWORD (lParam) ;

LocalUnlock (hRectData) ;

break ; // WM_SIZE must be processed by DefMDIChildProc

case WM_TIMER : // Display a random rectangle

hRectData = GetWindowWord (hwnd, 0) ;

npRectData = (NPRECTDATA) LocalLock (hRectData) ;

xLeft = rand () % npRectData->cxClient ;

xRight = rand () % npRectData->cxClient ;

yTop = rand () % npRectData->cyClient ;

yBottom = rand () % npRectData->cyClient ;

nRed = rand () & 255 ;

nGreen = rand () & 255 ;

nBlue = rand () & 255 ;

hdc = GetDC (hwnd) ;

hBrush = CreateSolidBrush (RGB (nRed, nGreen, nBlue)) ;

SelectObject (hdc, hBrush) ;

Rectangle (hdc, min (xLeft, xRight), min (yTop, yBottom),

max (xLeft, xRight), max (yTop, yBottom)) ;

ReleaseDC (hwnd, hdc) ;

DeleteObject (hBrush) ;

LocalUnlock (hRectData) ;

return 0 ;

case WM_PAINT : // Clear the window

InvalidateRect (hwnd, NULL, TRUE) ;

hdc = BeginPaint (hwnd, &ps) ;

EndPaint (hwnd, &ps) ;

return 0 ;

case WM_MDIACTIVATE : // Set the appropriate menu

if (wParam == TRUE)

SendMessage (hwndClient, WM_MDISETMENU, 0,

MAKELONG (hMenuRect, hMenuRectWindow)) ;

else

SendMessage (hwndClient, WM_MDISETMENU, 0,

MAKELONG (hMenuInit, hMenuInitWindow)) ;

DrawMenuBar (hwndFrame) ;

return 0 ;

case WM_DESTROY :

hRectData = GetWindowWord (hwnd, 0) ;

LocalFree (hRectData) ;

KillTimer (hwnd, 1) ;

return 0 ;

}

// Pass unprocessed message to DefMDIChildProc

return DefMDIChildProc (hwnd, message, wParam, lParam) ;

}

MDIDEMO.RC

/*----------------------------

MDIDEMO.RC resource script

----------------------------*/

#include <windows.h>

#include "mdidemo.h"

MdiMenuInit MENU

{

POPUP "&File"

{

MENUITEM "New &Hello", IDM_NEWHELLO

MENUITEM "New &Rectangles", IDM_NEWRECT

MENUITEM SEPARATOR

MENUITEM "E&xit", IDM_EXIT

}

}

MdiMenuHello MENU

{

POPUP "&File"

{

MENUITEM "New &Hello", IDM_NEWHELLO

MENUITEM "New &Rectangles", IDM_NEWRECT

MENUITEM "&Close", IDM_CLOSE

MENUITEM SEPARATOR

MENUITEM "E&xit", IDM_EXIT

}

POPUP "&Color"

{

MENUITEM "&Black", IDM_BLACK

MENUITEM "&Red", IDM_RED

MENUITEM "&Green", IDM_GREEN

MENUITEM "B&lue", IDM_BLUE

MENUITEM "&White", IDM_WHITE

}

POPUP "&Window"

{

MENUITEM "&Cascade\tShift+F5", IDM_CASCADE

MENUITEM "&Tile\tShift+F4", IDM_TILE

MENUITEM "Arrange &Icons", IDM_ARRANGE

MENUITEM "Close &All", IDM_CLOSEALL

}

}

MdiMenuRect MENU

{

POPUP "&File"

{

MENUITEM "New &Hello", IDM_NEWHELLO

MENUITEM "New &Rectangles", IDM_NEWRECT

MENUITEM "&Close", IDM_CLOSE

MENUITEM SEPARATOR

MENUITEM "E&xit", IDM_EXIT

}

POPUP "&Window"

{

MENUITEM "&Cascade\tShift+F5", IDM_CASCADE

MENUITEM "&Tile\tShift+F4", IDM_TILE

MENUITEM "Arrange &Icons", IDM_ARRANGE

MENUITEM "Close &All", IDM_CLOSEALL

}

}

MdiAccel ACCELERATORS

{

VK_F5, IDM_CASCADE, VIRTKEY, SHIFT

VK_F4, IDM_TILE, VIRTKEY, SHIFT

}

MDIDEMO.H

/*-----------------------

MDIDEMO.H header file

-----------------------*/

#define INIT_MENU_POS 0

#define HELLO_MENU_POS 2

#define RECT_MENU_POS 1

#define IDM_NEWHELLO 10

#define IDM_NEWRECT 11

#define IDM_CLOSE 12

#define IDM_EXIT 13

#define IDM_BLACK 20

#define IDM_RED 21

#define IDM_GREEN 22

#define IDM_BLUE 23

#define IDM_WHITE 24

#define IDM_TILE 30

#define IDM_CASCADE 31

#define IDM_ARRANGE 32

#define IDM_CLOSEALL 33

#define IDM_FIRSTCHILD 100

MDIDEMO.DEF

;------------------------------------

; MDIDEMO.DEF module definition file

;------------------------------------

NAME MDIDEMO

DESCRIPTION 'MDI Demonstration (c) Charles Petzold, 1990'

EXETYPE WINDOWS

STUB 'WINSTUB.EXE'

CODE PRELOAD MOVEABLE DISCARDABLE

DATA PRELOAD MOVEABLE MULTIPLE

HEAPSIZE 1024

STACKSIZE 8192

EXPORTS FrameWndProc

CloseEnumProc

HelloWndProc

RectWndProc

MDIDEMO supports two types of extremely simple document windows: One displays ”Hello, World!“ in the center of its client area, and the other displays a series of random rectangles. (In the source code listings and identifier names, these are referred to as the Hello document and the Rect document.) Different menus are associated with these two types of document windows. The document window that displays ”Hello, World!“ has a menu that allows you to change the color of the text.