http://www.codeproject.com/KB/winsdk/Sigma.aspx
这篇文章讲解了如何使用Win32 API创建一个基本的MDI程序。在这个例子中,被创建的MDI子窗体显示了如下
操作系统信息:计算机名、操作系统版本、补丁版本,CPU个数。
此外,这篇文章还包括:
*当打开子窗体时,更新菜单。
*创建多分割区域的状态栏。
*添加、移除托盘。
*使用邮槽在进程间通讯。
基于Win32 API 创建MDI程序
在WinMain()函数中,创建MDI框架窗体。在WM_CREATE处理代码中,创建了MDI的客户区。所有的子窗体
在客户区中浮动。
CLIENTCREATESTRUCT MDIClientCreateStruct; // Structure to be used for MDI client area switch(message) { case WM_CREATE: // On creation of main frame, create the MDI client area MDIClientCreateStruct.hWindowMenu = NULL; MDIClientCreateStruct.idFirstChild = IDM_FIRSTCHILD; ghMDIClientArea = CreateWindow(TEXT("MDICLIENT"), // predefined value for // MDI client area NULL, // no caption required WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE, 0, // No need to give any x/y or height/width. 0, 0, 0, hwnd, NULL, ghInstance, (void*) &MDIClientCreateStruct);
MDICLIENT 是第一个参数,预先被Windows定义。CreateWindow的最后一个参数是CLIENTCREATESTRUCT类型
的变量的指针。CLIENTCREATESTRUCT有两个成员:一个是程序窗体的菜单句柄,另一个是第一个子窗体的标识符。
当得到了命令消息ID_INFORMATION_SYSTEMINFORMATION 后,,创建显示系统信息的MDI子窗体。在堆上创
建CSystemInfo对象。G_pSystemInfo对象被用来创建窗体和填充树控件。CSystemInfo::CreateSystemInfoWindow()
注册了子窗体并创建了MDI子窗体。
MDICREATESTRUCT MDIChildCreateStruct; MDIChildCreateStruct.szClass = TEXT("SigmaSystemInfoWnd"); MDIChildCreateStruct.szTitle = TEXT("System Information"); MDIChildCreateStruct.hOwner = ghInstance; MDIChildCreateStruct.x = CW_USEDEFAULT; MDIChildCreateStruct.y = CW_USEDEFAULT; MDIChildCreateStruct.cx = CW_USEDEFAULT; MDIChildCreateStruct.cy = CW_USEDEFAULT; MDIChildCreateStruct.style = 0; MDIChildCreateStruct.lParam = 0; // m_hwndSystemInformation = (HWND) SendMessage(ghMDIClientArea, WM_MDICREATE, 0, (LPARAM) (LPMDICREATESTRUCT) &MDIChildCreateStruct); // return if its not possible to create the child window if(NULL == m_hwndSystemInformation) { return 0; }
MDICREATESTRUCT用来设置子窗体参数。SendMessage最后一个参数是它的指针。SendMessage发送WM_MDICREATE消息到主框架窗体的客户区。
一些类的功能
CSystemInfo:这个类有两个成员:CSystemInfoData成员,CSystemInfoView成员。在这个类中,创建了这个子窗体。
CSystemInfoData;这个类储存了树控件要显示的数据。
CSystemInfoView:这个类初始化了树控件,然后将CSystemInfoData中的数据填充到Tree中。
当子窗体被激活时,创建并改变菜单
框架窗体的菜单的资源是IDR_MAINFRAME_MENU.这个菜单在WinMain中加载。
ghMainFrameMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MAINFRAME_MENU)); ghSysInfoMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_SYSINFO_MENU)); DWORD derror = GetLastError(); //Create the main MDI frame window ghwndMainFrame = CreateWindow(gszSigmaFrameClassName, gszAppName, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, CW_USEDEFAULT, // allows system choose an x position CW_USEDEFAULT, // allows system choose a y position CW_USEDEFAULT, // width, CW_USEDEFAULT allows system to // choose height and width CW_USEDEFAULT, // height, CW_USEDEFAULT ignores heights // as this is set by setting // CW_USEDEFAULT in width above. NULL, // handle to parent window ghMainFrameMenu, // handle to menu hInstance, // handle to the instance of module NULL); // Long pointer to a value to be passed // to the window through the // CREATESTRUCT structure passed in the // lParam parameter the WM_CREATE message
子窗口菜单是IDR_SYSINFO_MENU。在子窗体的窗口函数中(SigmaSystemInfoWndProc)中有WM_MDIACTIVATE,此段代码用来修改子窗的菜单,
名为Window的菜单被添加。当子窗体失去焦点时,它将设置而框架窗体的菜单。DrawMenuBar()需要调用SendMessage发送WM_MDISETMENU
消息。一旦子窗体被打开,它的菜单由于调用EnableMenuItem而变灰。
case WM_MDIACTIVATE: { HWND hwndClient = GetParent(hWnd); HWND hwndFrame = GetParent(hwndClient); HMENU hSysInfoWindowMenu = GetSubMenu(ghSysInfoMenu, SIGMA_SYSINFO_WINDOW_MENU_POS) ; // Set the system info menu when getting activated if (lParam == (LPARAM) hWnd) { SendMessage(hwndClient, WM_MDISETMENU, (WPARAM) ghSysInfoMenu, (LPARAM) hSysInfoWindowMenu); //Gray out the system information menu item EnableMenuItem(ghSysInfoMenu, ID_INFORMATION_SYSTEMINFORMATION, MF_GRAYED); } // Set the frame window menu when losing focus if (lParam != (LPARAM) hWnd) { SendMessage(hwndClient, WM_MDISETMENU, (WPARAM) ghMainFrameMenu, (LPARAM) NULL) ; } // call DrawMenuBar after the menu items are set DrawMenuBar(hwndFrame);
状态栏和重排列的MDI客户区
在WinMain函数中,CreateStatusBar函数创建了状态栏。CreateWindowEx使用STATUSCLASSNAME创建了状态栏,如下所示
//Create the status bar ghwndStatusBar = CreateWindowEx(0, // extended not required STATUSCLASSNAME, // status bar class name, equivalent to // "msctls_statusbar32" "", //caption not required WS_CHILD | WS_VISIBLE, -100, // x -100, // y 10, // width 10, // height ghwndMainFrame, NULL, (HINSTANCE) GetWindowLong (ghwndMainFrame, GWL_HINSTANCE), NULL);
调用SendMessage(ghwndStatusBar, SB_SETPARTS, (WPARAM)nParts, (LPARAM)lpParts).详细过程如下:
//Create parts to the status bar RECT rcClient; LPINT lpParts = NULL; int nWidth = 0; int nParts = SIGMA_STATUSBAR_PARTS; // Get the coordinates of the parent window's client area. GetClientRect(ghwndMainFrame, &rcClient); // Allocate an array for holding the right edge coordinates. HLOCAL hloc = NULL; hloc = LocalAlloc(LHND, sizeof(int) * nParts); lpParts = (int *)LocalLock(hloc); // Calculate the right edge coordinate for each part, and // copy the coordinates to the array. nWidth = rcClient.right / nParts; for (int i = 0; i < nParts; i++) { lpParts[i] = nWidth; nWidth += nWidth; } // Create status bar parts. SendMessage(ghwndStatusBar, SB_SETPARTS, (WPARAM)nParts, (LPARAM)lpParts); // Free the array LocalUnlock(hloc); LocalFree(hloc);
添加系统托盘
在WinMain函数中,调用AddSysTrayIcon函数创建系统托盘。Shell_NotifyIcon添加了系统托盘的图标。
NOTIFYICONDATA nid = {0}; nid.cbSize = sizeof(nid); nid.uID = SIGMA_SYSTRAY_ICON_ID; // 0 to 12 are reserved and should not be used. nid.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE; nid.hIcon = LoadIcon(ghInstance, MAKEINTRESOURCE(IDI_SIGMA_MAIN_ICON)); strcpy(nid.szTip, "Sigma"); nid.hWnd = ghwndMainFrame; nid.uCallbackMessage = WM_SIGMA_SYSTRAY_MSG; //Add the notification to the tray Shell_NotifyIcon(NIM_ADD, &nid);
当关闭程序时,调用Shell_Notify(NIM_DELETE,&nid)移除托盘图标。
使用邮槽进行进程间通讯(IPC with Mailslot IPC = inter-process communication)
Me:与MDI窗体没有关系,不翻译了。
额外信息
*调用InitCommonControlsEx()函数初始化通用控件。这里创建了tree控件。
*框架窗体WM_SIZE的消息处理,不能调用DefFrameProc。因为调用DefFrameProc函数将引起MDI客户
区改变大小且重写状态栏。
*程序仅在XP SP3下通过测试。