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