MDIDemo程序分析

//没有文档窗口时,程序显示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);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值