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