C++ MDI 选择性子 Window 关闭

C++ MDI Selective Child Window Closing

您好,我正在尝试使用纯 Win32 API 开发一个 C++ MDI 程序(因为我需要该程序是可移植的,即 运行 甚至可以从 USB,尽可能多.exe 文件)。

此 MDI 程序有 2 种类型(classes)子 windows、"MdiPrinterChild" 和 "MdiStationChild." "MdiStationChild" window一旦主 window 的客户区被创建(只能创建一个 'Station' window),就会自动创建并显示。 "MdiPrinterChild" window 仅在用户从菜单中选择 "New Printer" 时创建,实际上可以创建多个 'printer' 子 windows。当 'printer' 子 window 处于活动状态时,可以触发 "close all" 菜单,据说可以关闭所有 'printer' windows(仅)。问题是,它还关闭了 'station' window。如果单击主要 window 的 'x',也会发生同样的事情。我尝试通过在 "CloseEnumProc" 中使用子 window 的 class 名称(if 语句)来选择性关闭子 windows,但这次的问题是,应用程序本身可以永远不会关闭。 'Exit' 来自菜单同样不会响应。

下面是 CloseEnumProc 的代码,但如果这段代码真的不可行,谁能给我一个示例代码如何做。谢谢

    #include <windows.h>
    #include <Winuser.h>
    #include <Winbase.h>
    #include "shlwapi.h"
    #include "CommCtrl.h"
    #include <tchar.h>
    #include "resource.h"
    #include "printer.h"

     #define INIT_MENU_POS          0 
     #define PRINTER_MENU_POS   2

     #define IDM_FIRSTCHILD   50000
     #define BUFSIZE MAX_PATH

     //prototypes
     LRESULT CALLBACK FrameWndProc  (HWND, UINT, WPARAM, LPARAM);
     BOOL    CALLBACK CloseEnumProc (HWND, LPARAM);
     LRESULT CALLBACK PrinterWndProc  (HWND, UINT, WPARAM, LPARAM);
     BOOL CALLBACK SetupStationDlgProc(HWND, UINT, WPARAM, LPARAM);
     LRESULT CALLBACK StationWndProc (HWND, UINT, WPARAM, LPARAM);

     //global variables
     TCHAR     szAppName[]    = TEXT ("VentureOEp");
     TCHAR     szFrameClass[] = TEXT ("MdiFrame");
     TCHAR     szPrinterClass[] = TEXT ("MdiPrinterChild");
     TCHAR     szStationClass[] = TEXT ("MdiStationChild");
     HINSTANCE hInst, hInstStation;
     HMENU     hMenuInit, hMenuPrinter;
     HMENU     hMenuInitWindow, hMenuPrinterWindow;

     int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
     {
     //locals to WinMain
     HWND hwndFrame;
     HWND hwndClient;
     MSG msg;
     WNDCLASSEX wc;
     HACCEL   hAccel;
     hInst = hInstance ;

     /*=====================*/
     //get current directory of this process and check if definition files are complete
TCHAR filePath[BUFSIZE] ;
DWORD dwRet ;

dwRet = GetCurrentDirectory(BUFSIZE, filePath) ;

if( dwRet == 0 )
    {       
      sprintf(filePath,"GetCurrentDirectory failed (%d)\n", GetLastError()) ;
        MessageBox(NULL,LPCSTR(filePath),LPCSTR("Get Current Directory Error!"),MB_ICONEXCLAMATION|MB_OK) ;
        return 0 ;
    }
  else
        {
            //check if definition files exists          

            char *exactPath ; 
            exactPath = (LPSTR)(filePath) ;

            strcat(exactPath, "\") ;
            strcat(exactPath, "Definition Files\printer.xml") ;

            if(!PathFileExists(exactPath))
            {
                MessageBox(NULL,_T("printer.xml not found"),_T("File check"),MB_ICONEXCLAMATION|MB_OK);
                return 0;
            }
        }

/*=====================*/
//check file access over network and copy to current process directory
int bFileExists = 0;
char buffer_1[]= "G:\Notebooks and Projects\Dev C++\Venture pOE\Definition Files_Source\printer.xml";
char* fileToCopy ;
const char *fileCopyTo ; 

fileToCopy = buffer_1 ;
fileCopyTo = "G:\Notebooks and Projects\Dev C++\Venture pOE\Definition Files\printer.xml" ;

if(!PathFileExists(fileToCopy))
    {
        MessageBox(NULL,_T("Network Access Failed"),_T("Access Check"),MB_ICONEXCLAMATION|MB_OK);
        return 0;
    }

if (!CopyFile(fileToCopy, fileCopyTo,FALSE))
    {
        MessageBox(NULL,_T("File update failed"),_T("Definitions update"),MB_ICONEXCLAMATION|MB_OK) ;
        return 0 ;
    }

/*=====================*/
//register frame window class
memset(&wc,0,sizeof(wc));
wc.cbSize                                       = sizeof(WNDCLASSEX);
wc.style                                        = CS_HREDRAW | CS_VREDRAW;
wc.cbClsExtra                               = 0;
wc.cbWndExtra                               = 0;
wc.lpfnWndProc                          = FrameWndProc;
wc.hInstance                                = hInstance;
wc.hCursor                                  = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground                        = HBRUSH(COLOR_WINDOW+1);
wc.lpszMenuName                         = NULL; //MAKEINTRESOURCE(IDR_MAINMENU);
wc.lpszClassName                        = szFrameClass;
wc.hIcon                                        = LoadIcon (NULL, IDI_APPLICATION); 
wc.hIconSm                                  = NULL; //HICON(LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_MAINICON), IMAGE_ICON, 16, 

if(!RegisterClassEx(&wc))
{
    MessageBox(NULL, _T("Frame Window Registration Failed!"),_T("Error!"),MB_ICONEXCLAMATION|MB_OK);
    return 0;
}

//register child (printer) window class (not the client window which is already pre-registered)

memset(&wc, 0, sizeof(wc));
wc.cbSize                                       = sizeof(WNDCLASSEX);
wc.style                                        = CS_HREDRAW | CS_VREDRAW; // | CS_NOCLOSE;
wc.lpfnWndProc                          = PrinterWndProc;
wc.cbClsExtra                               = 0;
wc.cbWndExtra                               = 0; 
wc.hInstance                                = hInstance;
wc.hCursor                                  = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground                        = HBRUSH(COLOR_WINDOW+1);
wc.lpszMenuName                         = NULL;
wc.lpszClassName                        = szPrinterClass;
wc.hIcon                                        = LoadIcon (NULL, IDI_APPLICATION); 
wc.hIconSm                                  = NULL; 

if(!RegisterClassEx(&wc))
    {
    MessageBox(NULL, _T("Printer Window Registration Failed!"),_T("Error!"),MB_ICONEXCLAMATION|MB_OK);
    return 0;
    }

 //register child (window) window class (not the client window which is already pre-registered)
memset(&wc, 0, sizeof(wc));
wc.cbSize                                       = sizeof(WNDCLASSEX);
wc.style                                        = CS_HREDRAW | CS_VREDRAW |  CS_NOCLOSE;
wc.lpfnWndProc                          = StationWndProc;
wc.cbClsExtra                               = 0;
wc.cbWndExtra                               = 0; 
wc.hInstance                                = hInstance;
wc.hCursor                                  = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground                        = HBRUSH(COLOR_WINDOW+1);
wc.lpszMenuName                         = NULL;
wc.lpszClassName                        = szStationClass;
wc.hIcon                                        = LoadIcon (NULL, IDI_APPLICATION); 
wc.hIconSm                                  = NULL; 

if(!RegisterClassEx(&wc))
    {
    MessageBox(NULL, _T("Station Window Registration Failed!"),_T("Error!"),MB_ICONEXCLAMATION|MB_OK);
    return 0;
    }

   //Obtain handles of menus
   hMenuInit  = LoadMenu (hInstance, TEXT ("MdiMenuInit")) ;
   hMenuPrinter = LoadMenu (hInstance, TEXT ("MdiMenuPrinter")) ;

   //Obtain handles of positions of submenu "Window"
   hMenuInitWindow  = GetSubMenu (hMenuInit,   INIT_MENU_POS) ;
   hMenuPrinterWindow = GetSubMenu (hMenuPrinter, PRINTER_MENU_POS) ;

   //Load accelerator table
   hAccel = LoadAccelerators (hInstance, szAppName) ;

   //create frame window
   //menu is loaded here
   hwndFrame = CreateWindowEx(
                        WS_EX_CLIENTEDGE,
                        szFrameClass,
                        _T("Venture Day Walker"),
                        WS_CLIPCHILDREN|WS_OVERLAPPEDWINDOW,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        NULL,
                        hMenuInit,
                        hInstance,
                        NULL);

if(hwndFrame == NULL)
{
    MessageBox(NULL, _T("Main Window Creation Failed!"),_T("Error!"),MB_ICONEXCLAMATION|MB_OK);
    return 0;
}

//get handle of the client window(as child of main window)
 hwndClient = GetWindow(hwndFrame,GW_CHILD);

//Display the window
ShowWindow(hwndFrame, nCmdShow);
UpdateWindow (hwndFrame);

// Enter the modified message loop (due to use of accelerators)
while (GetMessage (&msg, NULL, 0, 0))
    {
    if (!TranslateMDISysAccel (hwndClient, &msg) &&
       !TranslateAccelerator (hwndFrame, hAccel, &msg))
    {
  TranslateMessage (&msg) ;
  DispatchMessage (&msg) ;
    }
    }

// Clean up by deleting unattached menus
DestroyMenu (hMenuPrinter) ;


//end of WinMain
return msg.wParam;
}

    /*=====================*/
    //main window proc
    LRESULT CALLBACK FrameWndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
   static HWND        hwndClient ;
 CLIENTCREATESTRUCT clientcreate ;
 HWND               hwndChild, hwndChildStation ;
 MDICREATESTRUCT    mdicreate ;

 switch (message)
 {
 case WM_CREATE:          

       // Create the client window 
      clientcreate.hWindowMenu  = hMenuInitWindow ;
      clientcreate.idFirstChild = IDM_FIRSTCHILD ;

      hwndClient = CreateWindowEx (
                                                            WS_EX_CLIENTEDGE,
                                                                        TEXT("MDICLIENT"),
                                                                        NULL,
                                    WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE,   
                                    0, 0, 0, 0,
                                                                        hwnd,
                                                                        (HMENU) 1,
                                                                        hInst,
                                    (PSTR) &clientcreate) ;

                    // Create a station child window
        //provide values 1st for the MDICREATESTRUCT
        mdicreate.szClass = szStationClass;
        mdicreate.szTitle = TEXT ("Station Details");
        mdicreate.hOwner  = hInstStation;
        mdicreate.x       = CW_USEDEFAULT;
        mdicreate.y       = CW_USEDEFAULT;
        mdicreate.cx      = CW_USEDEFAULT;
        mdicreate.cy      = CW_USEDEFAULT;
        mdicreate.style   = 0; //WS_HSCROLL | WS_VSCROLL ;
        mdicreate.lParam  = 0;

        //sendmessage to create the child window using WM_MDICREATE and
        hwndChildStation = (HWND) SendMessage (hwndClient,
                               WM_MDICREATE, 0,
                               (LPARAM) (LPMDICREATESTRUCT) &mdicreate);
      break ;

 case WM_COMMAND:
      switch (LOWORD (wParam))
      {
      case IDM_STATION_SETUP:
            DialogBox(hInst, TEXT("IDD_SETUPSTATION"), hwnd, SetupStationDlgProc);
                         InvalidateRect (hwnd, NULL, TRUE) ; 
             break ;

      case IDM_PRINTER_NEW:       

           // Create a printer child window

           mdicreate.szClass = szPrinterClass ;
           mdicreate.szTitle = TEXT ("Serial Number + Model No.") ;
           mdicreate.hOwner  = hInst ;
           mdicreate.x       = CW_USEDEFAULT ;
           mdicreate.y       = CW_USEDEFAULT ;
           mdicreate.cx      = CW_USEDEFAULT ;
           mdicreate.cy      = CW_USEDEFAULT ;
           mdicreate.style   = WS_HSCROLL | WS_VSCROLL ; //or '0' for just frames
           mdicreate.lParam  = 0 ;

           hwndChild = (HWND) SendMessage (hwndClient,
                               WM_MDICREATE, 0,
                               (LPARAM) (LPMDICREATESTRUCT) &mdicreate);
           break ;

      case IDM_PRINTER_CLOSE: 

           // Close the active printer window
           hwndChild = (HWND) SendMessage (hwndClient,
                                           WM_MDIGETACTIVE, 0, 0) ;

           if (SendMessage (hwndChild, WM_QUERYENDSESSION, 0, 0))
                SendMessage (hwndClient, WM_MDIDESTROY, (WPARAM) hwndChild, 0) ;

           break ;

      case IDM_APP_EXIT:            

           // Exit the program
           SendMessage (hwnd, WM_CLOSE, 0, 0) ;
           break ;

           // messages for arranging windows
      case IDM_WINDOW_TILE:
           SendMessage (hwndClient, WM_MDITILE, 0, 0) ;
           break ;

      case IDM_WINDOW_CASCADE:
           SendMessage (hwndClient, WM_MDICASCADE, 0, 0) ;
           break ;

      case IDM_WINDOW_CLOSEALL:     // Attempt to close all children

           EnumChildWindows (hwndClient, CloseEnumProc, 0) ;
           break ;

                case IDM_ABOUT:     

           // About menu
                         MessageBox(NULL, _T("Thank You LORD!"),_T("Glory To GOD"),MB_ICONEXCLAMATION|MB_OK);
           break ;

      default:             

           // Pass to active child...
           hwndChild = (HWND) SendMessage (hwndClient, WM_MDIGETACTIVE, 0, 0) ;

           if (IsWindow (hwndChild))
                SendMessage (hwndChild, WM_COMMAND, wParam, lParam) ;

           return 0 ;        // ...and then to DefFrameProc
      }

      break ;

        case WM_PAINT:

                break ;

 case WM_QUERYENDSESSION:
 case WM_CLOSE:    //frame                  

                // Attempt to close all children 
      SendMessage (hwnd, WM_COMMAND, IDM_WINDOW_CLOSEALL, 0) ;

                //mark: if NULL, meaning no more child windows  
      if (NULL != GetWindow (hwndClient, GW_CHILD))
           return 0 ;

      break ;   // i.e., call DefFrameProc 

 case WM_DESTROY: //frame
      PostQuitMessage (0) ;

      break ;
 }

 // Pass unprocessed messages to DefFrameProc (not DefWindowProc)
 return DefFrameProc (hwnd, hwndClient, message, wParam, lParam) ;
   }


//'printer' window proc=========================================
LRESULT CALLBACK PrinterWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{

    static HWND     hwndClient, hwndFrame;
    HDC             hdc;
    HMENU           hMenu;
    PAINTSTRUCT     ps;
    RECT            rect;

    switch (message)
    {
    case WM_CREATE:

        // Save some window handles
        hwndClient = GetParent(hwnd);
        hwndFrame = GetParent(hwndClient);
        break;

    case WM_COMMAND:

        break;

    case WM_PAINT:

        break;

    case WM_MDIACTIVATE:

        // Set the Printer menu if gaining focus
        if (lParam == (LPARAM)hwnd)
            SendMessage(hwndClient, WM_MDISETMENU, (WPARAM)hMenuPrinter, (LPARAM)hMenuPrinterWindow);

        // Set the Init menu if losing focus
        if (lParam != (LPARAM)hwnd)
            SendMessage(hwndClient, WM_MDISETMENU, (WPARAM)hMenuInit, (LPARAM)hMenuInitWindow);

        DrawMenuBar(hwndFrame);

        break;

    case WM_QUERYENDSESSION:
    case WM_CLOSE: //printer

        if (IDOK != MessageBox(hwnd, TEXT("OK to close window?"), TEXT("Printer"), MB_ICONQUESTION | MB_OKCANCEL))
            return 0;

        break; // i.e., call DefMDIChildProc

    case WM_DESTROY: //printer

        break;
    }

    // Pass unprocessed message to DefMDIChildProc
    return DefMDIChildProc(hwnd, message, wParam, lParam);
}

//'Station' window proc=============================
LRESULT CALLBACK StationWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static HWND     hwndClient, hwndFrame;
    HDC             hdc;
    HMENU           hMenu;
    PAINTSTRUCT     ps;
    RECT            rect;

    switch (message)
    {
    case WM_CREATE:

        // Save some window handles
        hwndClient = GetParent(hwnd); 
        hwndFrame = GetParent(hwndClient);
        break;

    case WM_PAINT:

        break;

    case WM_MDIACTIVATE:

        break;

    case WM_DESTROY: //station

        break; //return 0 ;
    }

    // Pass unprocessed message to DefMDIChildProc
    return DefMDIChildProc(hwnd, message, wParam, lParam);
}

//'close all' CloseEnumProc =========================================
BOOL CALLBACK CloseEnumProc(HWND hwnd, LPARAM lParam)
{
    TCHAR className[20];
    int numChar = 20;

    // Check for icon title
    if (GetWindow(hwnd, GW_OWNER))
        return TRUE;

    GetClassName(hwnd, className, numChar);

    if (strcmp(szPrinterClass, className) == 0)
    {
        //if window is minimized, restore to previous size
        SendMessage(GetParent(hwnd), WM_MDIRESTORE, (WPARAM)hwnd, 0);

        //then send ask to close
        if (!SendMessage(hwnd, WM_QUERYENDSESSION, 0, 0))
            return TRUE;

        SendMessage(GetParent(hwnd), WM_MDIDESTROY, (WPARAM)hwnd, 0);
        return TRUE;
    }

}

CloseEnumProc:

更改CloseEnumProc如下:

BOOL CALLBACK CloseEnumProc(HWND hwnd, LPARAM lParam)
{
    const int numChar = 50;
    TCHAR className[numChar];

    GetClassName(hwnd, className, numChar);
    if (strcmp(szPrinterClass, className) == 0)
    {
        SendMessage(GetParent(hwnd), WM_MDIRESTORE, (WPARAM)hwnd, 0);
        SendMessage(hwnd, WM_CLOSE, 0, 0);

        //optional:
        //add this line incase there are multiple children, 
        //and you want to break the message after one CANCEL message
        if (IsWindow(hwnd)) return FALSE;
    }

    return TRUE;
}

检查 SendMessage(hwnd, WM_CLOSE, 0, 0); 的结果没有意义,它应该始终为零。如果有多个childwindows,并且如果用户决定不关闭childwindow,则IsWindow(hwnd)有效。您可能希望在那时打破循环。

FrameWndProc:

关闭主 window 时,您正在检查是否所有 children 在 IDM_WINDOW_CLOSEALL 消息后被销毁。但结果总是错误的,因为 "station" window 保持打开状态。

您应该看看是否还有一个以上 child window 剩余。如果只剩下一个child,那么那个child就是站window,你可以关闭它

LRESULT CALLBACK FrameWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    ...
    case WM_QUERYENDSESSION:
    case WM_CLOSE:
    {
        SendMessage(hwnd, WM_COMMAND, IDM_WINDOW_CLOSEALL, 0);
        int child_count = 0;
        HWND hchild = GetWindow(hwndClient, GW_CHILD);
        if (hchild)
        {
            child_count++;
            hchild = GetWindow(hchild, GW_HWNDNEXT);
            if (hchild)
            {
                child_count++;
            }
        }

        if (child_count < 2)
        {
            PostQuitMessage(0);
        }

        return 0;
    }
}

编辑

StationWndProc:

如果站 window 处于活动状态,则 IDM_PRINTER_CLOSE 将关闭 stationWnd。改变这个 stationWnd 过程是你不想要的:

LRESULT CALLBACK StationWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch(message)
    {
    case WM_CLOSE:
        return 0;
    default: break;
    }
return DefMDIChildProc(hwnd, message, wParam, lParam);
}