Search code examples
c++winapimdichildchildwindow

C++ MDI Selective Child Window Closing


Hi I'm trying to develop a c++ MDI program using purely Win32 API (because I need the program to be portable, i.e. can run even from USB, as much as possible only the .exe file).

This MDI program has 2 types (classes) of child windows, the "MdiPrinterChild" and "MdiStationChild." The "MdiStationChild" window is automatically created and shown once the client area of the main window is created (only one 'Station' window can be created). The "MdiPrinterChild" window is only created when the user selects "New Printer" from the menu and virtually can create several 'printer' child windows. When a 'printer' child window is active, a "close all" menu can be triggered, supposedly to close all 'printer' windows (only). The problem is, it's also closing the 'station' window. Same thing happens if click 'x' of the main window. I tried selective closing of child windows by using the class name of the child window (if statement), inside the "CloseEnumProc" but this time the problem, the application itself can never be closed. 'Exit' from menu will likewise not respond.

Below is the codef for CloseEnumProc, but if this code is really not workable, can anyone give me a sample code how to do it. Thanks.

    #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;
    }

}

Solution

  • CloseEnumProc:

    Change CloseEnumProc as follows:

    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;
    }
    

    There is no point in checking the result for SendMessage(hwnd, WM_CLOSE, 0, 0); it should always be zero. If there are multiple child windows, and if the user decided not to close the child window, then IsWindow(hwnd) is valid. You may wish to break the loop at that point.

    FrameWndProc:

    When closing the main window, you are checking to see if all children are destroyed after IDM_WINDOW_CLOSEALL message. But the result is always false because the "station" window remains open.

    You should instead see if there is more than one child window remaining. If there is only one child remaining, then that child is the station window and you can close it

    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;
        }
    }
    

    EDIT

    StationWndProc:

    If the station window is active, then IDM_PRINTER_CLOSE will close stationWnd. Change this the stationWnd proc is you don't want that:

    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);
    }