//没有文档窗口时,程序显示MdiMenuInit菜单,仅让创建新文档或是退出文档
#include <Windows.h>
#include "resource.h"
//指定Window子菜单在三个菜单模板中的位置,以此告诉客户窗口在哪儿放置文档列表
#define INIT_MENU_POS 0
#define HELLO_MENU_POS 2
#define RECT_MENU_POS 1
//给将要出现在Window子菜单中的列表内的第一个文档窗口的,应该比其他所有菜单ID都大。
#define IDM_FIRSTCHILD 50000
LRESULT CALLBACK FrameWndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam);
BOOL CALLBACK CloseEnumProc(HWND hwnd,LPARAM lParam);
LRESULT CALLBACK HelloWndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam);
LRESULT CALLBACK RectWndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam);
//structure for storing data unique to each Hello Child window
typedef struct tagHELLEDATA
{
UINT iColor; //Color菜单中某个菜单ID
COLORREF clrText; //文本颜色
}HELLODATA,*PHELLODATA;
//structure for storing data unique to each Rect Child window
typedef struct tagRECTDATA
{
short cxClient;
short cyClient;
}RECTDATA,*PRECTDATA;
//global variable
TCHAR szAppName[]=TEXT("MDIDemo");
TCHAR szFrameClass[]=TEXT("MdiFrame");
TCHAR szHelloClass[]=TEXT("MdiHelloChild");
TCHAR szRectClass[]=TEXT("MdiRectChild");
HINSTANCE hInst;
HMENU hMenuInit,hMenuHello,hMenuRect;
HMENU hMenuInitWindow,hMenuHelloWindow,hMenuRectWindow;
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,PSTR szCmdLine,int icmdShow)
{
HWND hwndFrame,hwndClient;
MSG msg;
WNDCLASS wndclass;
hInst=hInstance;
//Register the frame window class
wndclass.style=CS_HREDRAW|CS_VREDRAW;
wndclass.lpfnWndProc=FrameWndProc;
wndclass.cbClsExtra=0;
wndclass.cbWndExtra=0;
wndclass.hCursor=LoadCursor(NULL,IDC_ARROW);
wndclass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
wndclass.hbrBackground=(HBRUSH)(COLOR_APPWORKSPACE+1);
wndclass.hInstance=hInstance;
wndclass.lpszMenuName=NULL;
wndclass.lpszClassName=szFrameClass;
if (!RegisterClass(&wndclass))
{
MessageBox(NULL,TEXT("ERROR"),TEXT(""),MB_ICONERROR);
return 0;
}
//Register the Hello Child window class
wndclass.style=CS_HREDRAW|CS_VREDRAW;
wndclass.lpfnWndProc=HelloWndProc;
wndclass.cbClsExtra=0;
wndclass.cbWndExtra=sizeof(HANDLE); //存放指向一个内存块(大小为HELLODATA或RECTDATA结构的大小)的指针
wndclass.hCursor=LoadCursor(NULL,IDC_ARROW);
wndclass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.hInstance=hInstance;
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(HANDLE);
wndclass.hCursor=LoadCursor(NULL,IDC_ARROW);
wndclass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.hInstance=hInstance;
wndclass.lpszMenuName=NULL;
wndclass.lpszClassName=szRectClass;
RegisterClass(&wndclass);
//Obtain handles to three possible menus & submenus
//获取三个菜单句柄
hMenuInit=LoadMenu(hInstance,MAKEINTRESOURCE(MDIMENUINIT));
hMenuHello=LoadMenu(hInstance,MAKEINTRESOURCE(MDIMENUHELLO));
hMenuRect=LoadMenu(hInstance,MAKEINTRESOURCE(MDIMENURECT));
//得到Window子菜单的句柄
hMenuInitWindow=GetSubMenu(hMenuInit,INIT_MENU_POS);
hMenuHelloWindow=GetSubMenu(hMenuHello,HELLO_MENU_POS);
hMenuRectWindow=GetSubMenu(hMenuRect,RECT_MENU_POS);
//create the frame window
hwndFrame=CreateWindow(szFrameClass,szAppName,WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN,CW_USEDEFAULT,CW_USEDEFAULT,
CW_USEDEFAULT,CW_USEDEFAULT,NULL,hMenuInit,hInstance,NULL);
//获取客户窗口句柄
//hwndClient在hwndFrame处理WM_CREATE时已创建
hwndClient=GetWindow(hwndFrame,GW_CHILD);
ShowWindow(hwndFrame,icmdShow);
UpdateWindow(hwndFrame);
//Enter the modified message loop
while (GetMessage(&msg,NULL,0,0))
{
//TranslateMDISysAccel把对应到特殊的MDI加速键的任何按键都翻译成WM_SYSCOMMAND消息。若返回TRUE,表明已被其翻译过
if (!TranslateMDISysAccel(hwndClient,&msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
//Clean up by deleting unattached menus
DestroyMenu(hMenuHello);
DestroyMenu(hMenuRect);
return (int)msg.wParam;
}
LRESULT CALLBACK FrameWndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
static HWND hwndClient;
CLIENTCREATESTRUCT clientcreate;
HWND hwndChild;
MDICREATESTRUCT mdicreate;
switch (message)
{
case WM_CREATE://框架窗口创建客户窗口
//create the client window
//hWindowMenu是要把文档列表添加在其后的子菜单的句柄。
//idFirstChild是欲文档列表中第一个文档窗口相关联的菜单ID。即IDM_FIRSTCHILD
clientcreate.hWindowMenu=hMenuInitWindow;
clientcreate.idFirstChild=IDM_FIRSTCHILD;
//程序调用预定义的窗口类MDICLIENT的“客户窗口”。
//CreateWindow的最后一个参数是一个指向CLIENTCREATESTRUCT结构的指针。
//同时,与框架窗口和子窗口不同,客户窗口不需要窗口过程,因为已经事先注册好
hwndClient=CreateWindow(TEXT("MDICLIENT"),NULL,WS_CHILD|WS_CLIPCHILDREN|WS_VISIBLE,0,0,0,0,hwnd,(HMENU)1,
hInst,(PSTR)&clientcreate);
return 0;
//创建文档窗口(子窗口)需要初始化一个MDICREATESTRUCT结构,然后向客户窗口发送一个WM_MDICREATE消息,
//并用指针指向这个被创建的结构
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDM_FILE_NEWHELLO: //create a Hello child window
mdicreate.szClass=szHelloClass;
mdicreate.szTitle=TEXT("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=0; //给框架窗口和子窗口提供共享某些变量的方法
//给客户窗口发送WM_MDICREATE消息,LPARAM为指向该结构的指针,然后客户窗口会创建子文档窗口。
//客户窗口收到消息后,把子窗口的标题加到用来创建客户窗口的MDICLIENTSTRUCT所指定的子菜单的下面。
//当创建第一个子文档窗口时,该子菜单就是MdiMenuInit菜单的File子菜单。
hwndChild=(HWND)SendMessage(hwndClient,WM_MDICREATE,0,(LPARAM)(LPMDICREATESTRUCT)&mdicreate);
return 0;
case IDM_FILE_NEWRECT: //Create a Rect child window
mdicreate.szClass=szRectClass;
mdicreate.szTitle=TEXT("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=0;
hwndChild=(HWND)SendMessage(hwndClient,WM_MDICREATE,0,(LPARAM)(LPMDICREATESTRUCT)&mdicreate);
return 0;
case IDM_FILE_CLOSE: //Close the active window
//通过向客户窗口发送消息来获得活动子窗口的句柄。
hwndChild=(HWND)SendMessage(hwndClient,WM_MDIGETACTIVE,0,0);
//如果子窗口确实对WM_QUERYENDSESSION消息做出了响应,程序就给客户窗口发送WM_MDIDESTROY消息关闭子窗口
if (SendMessage(hwndChild,WM_QUERYENDSESSION,0,0))
{
SendMessage(hwndClient,WM_MDIDESTROY,(WPARAM)hwndChild,0);
}
return 0;
case IDM_APP_EXIT: //Exit the program
SendMessage(hwnd,WM_CLOSE,0,0);
return 0;
//***子窗口发送的消息???????????
//给客户窗口发送消息
//messages for arranging windows
case IDM_WINDOW_TILE: //平铺
SendMessage(hwndClient,WM_MDITILE,0,0);
return 0;
case IDM_WINDOW_CASCADE: //层叠
SendMessage(hwndClient,WM_MDICASCADE,0,0);
return 0;
case IDM_WINDOW_ARRANGE: //排列图标(排列最小化的图标)
SendMessage(hwndClient,WM_MDIICONARRANGE,0,0);
return 0;
case IDM_WINDOW_CLOSEALL: //Attempt to close all children
//调用EnumChildWindows,传给它一个指向CloseEnumProc函数的指针
EnumChildWindows(hwndClient,CloseEnumProc,0);
return 0;
default: //Pass to active child...(注意:是活动窗口)
//FrameWndProc不处理任何代表某种颜色被Color菜单选中了的WM_COMMAND消息,这些消息是文档窗口应该负责的。
//因此,FrameWndProc把所有没处理的WM_COMMAND消息都转发给活动的窗口,以便子窗口能处理跟它相关的消息。
hwndChild=(HWND)SendMessage(hwndClient,WM_MDIGETACTIVE,0,0);
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_WINDOW_CLOSEALL,0);
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)
//所有框架窗口选择不予处理的消息都必须传给DefFrameProc。
//对所有未处理的消息,不再调用DefWindowProc,而是在框架窗口过程中调用DefFrameProc,在子窗口中调用DefMDIChildProc
return DefFrameProc(hwnd,hwndClient,message,wParam,lParam);
}
BOOL CALLBACK CloseEnumProc(HWND hwnd,LPARAM lParam)
{
// hwnd 为客户窗口中所枚举的一个子窗口句柄
// GW_OWNER:返回的句柄标识了指定窗口的所有者窗口句柄
// 只有一个重叠或弹出式窗口可以是所有者窗口,一个子 窗口不能是所有者窗口
// 依次作为有效性检查
The system automatically destroys an owned window when its owner is destroyed
//An owned window is hidden when its owner is minimized
//Only an overlapped or pop-up window can be an owner window; a child window cannot be an owner window.
if (GetWindow(hwnd,GW_OWNER)) //check for icon title
{
return TRUE;
}
// GetParent(hwnd)获得客户窗口的句柄
// WM_MDIRESTORE 最大化或还原为最小规模的MDI子窗口。
// (WPARAM) hwnd 被恢复的子窗口句柄
SendMessage(GetParent(hwnd),WM_MDIRESTORE,(WPARAM)hwnd,0);
// 向子窗口发送关闭消息,根据返回值来操作
if (!SendMessage(GetParent(hwnd),WM_QUERYENDSESSION,0,0))
{
return TRUE;
}
// 将WM_MDIDESTROY消息发送给客户窗口,从而关闭指定的子窗口
SendMessage(GetParent(hwnd),WM_MDIDESTROY,(WPARAM)hwnd,0);
return TRUE;
}
LRESULT CALLBACK HelloWndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM 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;
PHELLODATA pHelloData;
PAINTSTRUCT ps;
RECT rect;
switch (message)
{
case WM_CREATE:
//Allocate memory for window private data
//分配指向预留内存,初始化其两个字段,并用SetWindowLong来存储这个指针。
pHelloData=(PHELLODATA)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(HELLODATA));
pHelloData->iColor=IDM_COLOR_BLACK;
pHelloData->clrText=RGB(0,0,0);
SetWindowLong(hwnd,0,(long)pHelloData);
//Save some window handles
hwndClient=GetParent(hwnd);
hwndFrame=GetParent(hwndClient);
return 0;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDM_COLOR_BLACK:
case IDM_COLOR_RED:
case IDM_COLOR_GREEN:
case IDM_COLOR_BLUE:
case IDM_COLOR_WHITE:
//Change the text color.
//得到指向含有HELLODATA结构的内存块的指针,利用它,会取消选中已选定的菜单项,再选中所选择的菜单项,并保存新颜色。
pHelloData=(PHELLODATA)GetWindowLong(hwnd,0);
hMenu=GetMenu(hwndFrame);
CheckMenuItem(hMenu,pHelloData->iColor,MF_UNCHECKED);
pHelloData->iColor=wParam;
CheckMenuItem(hMenu,pHelloData->iColor,MF_CHECKED);
pHelloData->clrText=clrTextArray[wParam-IDM_COLOR_BLACK];
InvalidateRect(hwnd,NULL,FALSE);
}
return 0;
case WM_PAINT:
//Paint the window
hdc=BeginPaint(hwnd,&ps);
pHelloData=(PHELLODATA)GetWindowLong(hwnd,0);
SetTextColor(hdc,pHelloData->clrText);
GetClientRect(hwnd,&rect);
DrawText(hdc,TEXT("Hello,World"),-1,&rect,DT_SINGLELINE|DT_CENTER|DT_VCENTER);
EndPaint(hwnd,&ps);
return 0;
//只要窗口变成活动或非活动状态(看lParam是不是包含窗口的句柄),就收到此消息。
//另外,此消息给文档窗口提供了改变菜单的机会:
//如果lParam含有窗口的句柄(表示窗口变成活动状态),HelloWndProc会把菜单变成MdiMenuHello。
//如果lParam含有另一个窗口的句柄,就把菜单变成MdiMenuInit。
//For more details,look it up in the MSDN:
//客户窗口接收到此消息后,把它发送给being deactivated and being activated的文档窗口。
//文档窗口接收到此消息是,参数变为:WPARAM为being deactivated窗口,LPARAM为being activated窗口。
case WM_MDIACTIVATE:
//Set the Hello menu if gaining focus
if (lParam==(LPARAM)hwnd)
{
//wParam:Handle to the new frame window menu
//lParam:Handle to the new window menu.
SendMessage(hwndClient,WM_MDISETMENU,(WPARAM)hMenuHello,(LPARAM)hMenuHelloWindow);
}
//Check or uncheck menu item
pHelloData=(PHELLODATA)GetWindowLong(hwnd,0);
CheckMenuItem(hMenuHello,pHelloData->iColor,(lParam==(LPARAM)hwnd)?MF_CHECKED:MF_UNCHECKED);
//Set the Init menu if losing focus
if (lParam!=(LPARAM)hwnd)
{
//通过给客户窗口发送WM_MDISETMENU消息来改变菜单。在MDI程序中,不要用SetMenu函数来改变菜单。
SendMessage(hwndClient,WM_MDISETMENU,(WPARAM)hMenuInit,(LPARAM)hMenuInitWindow);
}
//After sending WM_MDISETMENU, an application must call the DrawMenuBar function to update the menu bar.
DrawMenuBar(hwndFrame);
return 0;
case WM_QUERYENDSESSION:
case WM_CLOSE:
if (IDOK!=MessageBox(hwnd,TEXT("OK to close window?"),TEXT("Hello"),MB_ICONQUESTION|MB_OKCANCEL))
{
return 0;
}
break; //i.e.., call DefMDIChildProc
case WM_DESTROY:
pHelloData=(PHELLODATA)GetWindowLong(hwnd,0);
HeapFree(GetProcessHeap(),0,pHelloData);
return 0;
}
//Pass unprocessed message to DefMDIChildProc
return DefMDIChildProc(hwnd,message,wParam,lParam);
}
LRESULT CALLBACK RectWndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
static HWND hwndClient,hwndFrame;
HBRUSH hBrush;
HDC hdc;
PRECTDATA pRectData;
PAINTSTRUCT ps;
int xLeft,xRight,yTop,yBottom;
short nRed,nGreen,nBlue;
switch (message)
{
case WM_CREATE:
//Allocate memory for window private data
pRectData=(PRECTDATA)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(RECTDATA));
SetWindowLong(hwnd,0,(long)pRectData);
//Start the timer going
SetTimer(hwnd,1,250,NULL);
//Save some window handles
hwndClient=GetParent(hwnd);
hwndFrame=GetParent(hwndClient);
return 0;
case WM_SIZE: //If not minimized,save the window size
if (wParam!=SIZE_MINIMIZED)
{
pRectData=(PRECTDATA)GetWindowLong(hwnd,0);
pRectData->cxClient=LOWORD(lParam);
pRectData->cyClient=HIWORD(lParam);
}
break; // WM_SIZE must be processed by DefMDIChildProc
case WM_TIMER: //Display a random rectangle
pRectData=(PRECTDATA)GetWindowLong(hwnd,0);
xLeft=rand()%pRectData->cxClient;
xRight=rand()%pRectData->cxClient;
yTop=rand()%pRectData->cyClient;
yBottom=rand()%pRectData->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);
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 (lParam==(LPARAM)hwnd)
{
SendMessage(hwndClient,WM_MDISETMENU,(WPARAM)hMenuRect,(LPARAM)hMenuRectWindow);
}
else
{
SendMessage(hwndClient,WM_MDISETMENU,(WPARAM)hMenuInit,(LPARAM)hMenuInitWindow);
}
DrawMenuBar(hwndFrame);
return 0;
case WM_DESTROY:
pRectData=(PRECTDATA)GetWindowLong(hwnd,0);
HeapFree(GetProcessHeap(),0,pRectData);
KillTimer(hwnd,1);
return 0;
}
//Pass unprocessed message to DefMDIChildProc
return DefMDIChildProc(hwnd,message,wParam,lParam);
}
MDIDemo程序分析
最新推荐文章于 2022-07-01 14:46:59 发布