Code:
//Main.h
#ifndef MAIN_H
#define MAIN_H
#define dim(x) (sizeof(x) / sizeof(x[0]))
#define IDC_BUTTON_FORM1 1600
#define IDC_BUTTON_FORM2 1605
#define IDC_BUTTON_FORM3 1610
struct WndEventArgs
{
HWND hWnd;
WPARAM wParam;
LPARAM lParam;
HINSTANCE hIns;
};
struct EVENTHANDLER
{
unsigned int iMsg;
LRESULT (*fnPtr)(WndEventArgs&);
};
LRESULT fnWndProc_OnCreate (WndEventArgs&); //We need foreward declarations of
LRESULT fnWndProc_OnCommand (WndEventArgs&); //the various functions in a main
LRESULT fnWndProc_OnClose (WndEventArgs&); //header file so that the event
//handlers can be attached below.
LRESULT fnForm1_OnCreate (WndEventArgs&);
LRESULT fnForm1_OnPaint (WndEventArgs&);
LRESULT fnForm1_OnDestroy (WndEventArgs&);
LRESULT fnForm2_OnCreate (WndEventArgs&);
LRESULT fnForm2_OnPaint (WndEventArgs&);
LRESULT fnForm2_OnDestroy (WndEventArgs&);
LRESULT fnForm3_OnCreate (WndEventArgs&);
LRESULT fnForm3_OnPaint (WndEventArgs&);
LRESULT fnForm3_OnClose (WndEventArgs&);
const EVENTHANDLER Form1EventHandler[]=
{
{WM_CREATE, fnForm1_OnCreate},
{WM_PAINT, fnForm1_OnPaint},
{WM_DESTROY, fnForm1_OnDestroy}
};
const EVENTHANDLER Form2EventHandler[]=
{
{WM_CREATE, fnForm2_OnCreate},
{WM_PAINT, fnForm2_OnPaint},
{WM_DESTROY, fnForm2_OnDestroy}
};
const EVENTHANDLER Form3EventHandler[]=
{
{WM_PAINT, fnForm3_OnPaint},
{WM_CLOSE, fnForm3_OnClose}
};
#endif
Code:
// Main.cpp
// cl Main.cpp Form1.cpp Form2.cpp Form3.cpp /O1 /Os /GS- /W2 /FeMultipleForms.exe TCLib.lib kernel32.lib user32.lib gdi32.lib
// cl Main.cpp Form1.cpp Form2.cpp Form3.cpp /O1 /Os /GS- /MT /FeMultipleForms.exe kernel32.lib user32.lib gdi32.lib
// 92,160 Bytes x64 VC19 (VS 2015) asci libcmt linked
// 10,752 Bytes x64 VC19 (VS 2015) wide TCLib linked
// 9,728 bytes x64 VC19 (VS 2015) asci TCLib linked
#ifndef UNICODE
#define UNICODE
#endif
#ifndef _UNICODE
#define _UNICODE
#endif
// Main.cpp //Program displays a main Form/Window/Dialog With Three Buttons on it to simulate
#include <windows.h> //a program started from a main Form with three modules/options/modalities within
#include <tchar.h> //which it operates. When you click the top button a new Form/Window/Dialog is
#include <stdio.h> //created with CreateWindow() of the "Form1" Window Class, and in the WM_CREATE
#include "Main.h" //handler of this new window it disables the main form with the three buttons and
#include "Form1.h" //therefore is an example of a modal dialog. When you dismiss this modal dialog
#include "Form2.h" //and click on Button/Option #2 on the Main Form, a CreateWindow() call creates a
#include "Form3.h" //window of "Form2" Window Class, and in the WM_CREATE handler it sets the visi-
// bility of the main window to FALSE.
const EVENTHANDLER EventHandler[]= //Since we foreward declared above
{ //the various event handling functions
{WM_CREATE, fnWndProc_OnCreate}, //of the various forms, windows, dialogs
{WM_COMMAND, fnWndProc_OnCommand}, //above, we can fill out the fields of
{WM_CLOSE, fnWndProc_OnClose} //our EVENTHANDLER structures for the
}; //various objects.
LRESULT fnWndProc_OnCreate(WndEventArgs& Wea) //...for this window it hides or makes invisible the main
{ //window. After dismssing this window you'll find that you
DWORD dwStyle=WS_CHILD|WS_VISIBLE; //can click on the Option #3 button as many times as you like
TCHAR szClassName[16]; //because the window-form-dialog it creates neither disables
WNDCLASSEX wc; //nor makes invisible the main window. Further note that these
//Option #3 windows can be interacted with regardless of what-
Wea.hIns=((LPCREATESTRUCT)Wea.lParam)->hInstance; //ever is going on with the other windows.
CreateWindow(_T("button"),_T("Option #1"),dwStyle,65,15,120,25,Wea.hWnd,(HMENU)IDC_BUTTON_FORM1,Wea.hIns,0);
CreateWindow(_T("button"),_T("Option #2"),dwStyle,65,55,120,25,Wea.hWnd,(HMENU)IDC_BUTTON_FORM2,Wea.hIns,0);
CreateWindow(_T("button"),_T("Option #3"),dwStyle,65,95,120,25,Wea.hWnd,(HMENU)IDC_BUTTON_FORM3,Wea.hIns,0);
//Register Window Classes For Form1, Form2 and Form3
wc.cbSize=sizeof(WNDCLASSEX), wc.style=CS_HREDRAW | CS_VREDRAW;
wc.cbClsExtra=0, wc.cbWndExtra=0;
wc.hInstance=Wea.hIns, wc.hIcon=LoadIcon(NULL, IDI_APPLICATION);
wc.hIconSm=0, wc.hCursor=LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH), wc.lpszMenuName=NULL;
_tcscpy(szClassName,_T("Form1")), wc.lpszClassName=szClassName;
wc.lpfnWndProc=fnForm1_WndProc;
RegisterClassEx(&wc);
_tcscpy(szClassName,_T("Form2"));
wc.lpfnWndProc=fnForm2_WndProc;
wc.lpszClassName=szClassName; //Note that a WM_CREATE call is akin to a constructor call in typical
RegisterClassEx(&wc); //C++ class architecture. When you receive this call/message Windows
//has finished doing what it needs to support the Window object, and
_tcscpy(szClassName,_T("Form3")); //is 'passing the ball' to you. In my apps with multiple windows I
wc.lpszClassName=szClassName; //typically use the WM_CREATE handler to register any window classes
wc.lpfnWndProc=fnForm3_WndProc; //I need in the app, so that I can make CreateWindow() calls when I
RegisterClassEx(&wc); //need to instantiate a window of some type.
return 0;
}
long btnForm1_Click(WndEventArgs& Wea) //This is an 'Event Handler' for a click of the top button on the
{ //main Form. It uses CreateWindowEx() to instantiate a new Window
DWORD dwStyle,dwExStyle; //of class "Form1" Note that the last parameter of the call is
HWND hDlg; //Wea.hWnd. That is the HWND of the main program Window. The
MSG Msg; //last parameter is the Creation Parameters parameter. It can be
//retrieved (as can all the others) through the CREATIONSTRUCT a
dwStyle = WS_CAPTION | WS_POPUPWINDOW | WS_VISIBLE; //pointer to which is received in the lParam of the WM_CREATE
dwExStyle = WS_EX_DLGMODALFRAME | WS_EX_CONTROLPARENT; //message for the newly instantiating Window.
hDlg=CreateWindowEx(dwExStyle,_T("Form1"),_T("Form1"),dwStyle,50,25,310,185,Wea.hWnd,(HMENU)0,GetModuleHandle(0),Wea.hWnd);
ShowWindow(hDlg,SW_SHOWNORMAL);
UpdateWindow(hDlg);
while(GetMessage(&Msg,NULL,0,0)) // The IsDialogMessage function determines whether a message is intended for the specified
{ // dialog box and, if it is, processes the message. If the message has been processed, the
if(!IsDialogMessage(hDlg,&Msg)) // return value is nonzero. If the message has not been processed, the return value is zero.
{ // Because the IsDialogMessage function performs all necessary translating and dispatching of
TranslateMessage(&Msg); // messages, a message processed by IsDialogMessage must not be passed to the TranslateMessage
DispatchMessage(&Msg); // or DispatchMessage function.If the return value is zero, its not a message for hDlg. So
} // process it.
}
return 0;
}
long btnForm2_Click(WndEventArgs& Wea)
{
HWND hWnd;
hWnd=CreateWindowEx
(
0,
_T("Form2"),
_T("Form2"),
WS_OVERLAPPEDWINDOW,
200,250,310,185,
0,
(HMENU)0,
GetModuleHandle(0),
Wea.hWnd
);
ShowWindow(hWnd,SW_SHOWNORMAL);
UpdateWindow(hWnd);
return 0;
}
long btnForm3_Click(WndEventArgs& Wea)
{
HWND hWnd;
hWnd=CreateWindowEx
(
0,
_T("Form3"),
_T("Form3"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,CW_USEDEFAULT,300,260,
0,
(HMENU)0,
GetModuleHandle(0),
Wea.hWnd
);
ShowWindow(hWnd,SW_SHOWNORMAL);
UpdateWindow(hWnd);
return 0;
}
LRESULT fnWndProc_OnCommand(WndEventArgs& Wea)
{
switch(LOWORD(Wea.wParam))
{
case IDC_BUTTON_FORM1:
return btnForm1_Click(Wea);
case IDC_BUTTON_FORM2:
return btnForm2_Click(Wea);
case IDC_BUTTON_FORM3:
return btnForm3_Click(Wea);
}
return 0;
}
LRESULT fnWndProc_OnClose(WndEventArgs& Wea) //Search And Destroy Mission For Any Form3
{ //Windows Hanging Around.
HWND hForm;
if(MessageBox(Wea.hWnd,_T("Do You Wish To Exit?"),_T("Exit App?"),MB_YESNO)==IDYES)
{
do //If FindWindow() returns something other
{ //than zero, it found a window matching
hForm=FindWindow(_T("Form3"),_T("Form3")); //the description of what you are looking
if(hForm) //for. In that case, send a WM_CLOSE
SendMessage(hForm,WM_CLOSE,0,0); //message. If NULL is returned then just
else //break out of the loop and terminate the
break; //app. I don't believe this code is really
}while(TRUE); //necessary, but I left it in.
DestroyWindow(Wea.hWnd);
PostQuitMessage(0);
}
return 0;
}
LRESULT CALLBACK fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam,LPARAM lParam)
{
WndEventArgs Wea;
for(unsigned int i=0; i<dim(EventHandler); i++)
{
if(EventHandler[i].iMsg==msg)
{
Wea.hWnd=hwnd, Wea.lParam=lParam, Wea.wParam=wParam;
return (*EventHandler[i].fnPtr)(Wea);
}
}
return (DefWindowProc(hwnd, msg, wParam, lParam));
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
TCHAR szClassName[]=_T("Multiple Forms");
WNDCLASSEX wc={};
MSG messages;
HWND hWnd;
wc.lpszClassName=szClassName; wc.lpfnWndProc=fnWndProc;
wc.cbSize=sizeof (WNDCLASSEX); wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);
wc.hInstance=hInstance, wc.hCursor=LoadCursor(NULL,IDC_ARROW);
wc.hbrBackground=(HBRUSH)COLOR_BTNSHADOW;
RegisterClassEx(&wc);
hWnd=CreateWindowEx(0,szClassName,szClassName,WS_OVERLAPPEDWINDOW,250,500,260,180,HWND_DESKTOP,0,hInstance,0);
ShowWindow(hWnd,iShow);
while(GetMessage(&messages,NULL,0,0))
{
TranslateMessage(&messages);
DispatchMessage(&messages);
}
return messages.wParam;
}
Code:
//Form1.cpp
#ifndef UNICODE
#define UNICODE
#endif
#ifndef _UNICODE
#define _UNICODE
#endif
#include <Windows.h>
#include <tchar.h>
#include "Form1.h"
#include "Main.h"
LRESULT fnForm1_OnCreate(WndEventArgs& Wea)
{
CREATESTRUCT* pCreateStruct;
HWND hMain;
pCreateStruct=(CREATESTRUCT*)Wea.lParam;
hMain=(HWND)pCreateStruct->lpCreateParams;
SetWindowLongPtr(Wea.hWnd,GWLP_USERDATA,(LONG_PTR)hMain);
EnableWindow(hMain,FALSE);
return 0;
}
LRESULT fnForm1_OnPaint(WndEventArgs& Wea)
{
PAINTSTRUCT ps;
HDC hDC;
hDC=BeginPaint(Wea.hWnd,&ps);
TextOut(hDC,0,0,_T("This Is Form1. It Disables The Main"),36);
TextOut(hDC,0,16,_T("Window, And That Makes It Modal. Note"),38);
TextOut(hDC,0,32,_T("That We Passed The Handle Of The Main"),37);
TextOut(hDC,0,48,_T("Window In The Last Parameter Of The"),35);
TextOut(hDC,0,64,_T("CreateWindow() Call, And Retrieved It In"),40);
TextOut(hDC,0,80,_T("fnForm1_OnCreate(). We Then Stored It So"),41);
TextOut(hDC,0,96,_T("We Could EnableWindow(TRUE) The Main"),36);
TextOut(hDC,0,112,_T("Window When This Modal Form Is"),30);
TextOut(hDC,0,128,_T("Dismissed."),10);
EndPaint(Wea.hWnd,&ps);
return 0;
}
LRESULT fnForm1_OnDestroy(WndEventArgs& Wea)
{
HWND hMain;
hMain=(HWND)GetWindowLongPtr(Wea.hWnd,GWLP_USERDATA);
EnableWindow(hMain,TRUE);
PostQuitMessage(0);
return 0;
}
LRESULT CALLBACK fnForm1_WndProc(HWND hWnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
WndEventArgs Wea;
for(unsigned int i=0; i<dim(Form1EventHandler); i++)
{
if(Form1EventHandler[i].iMsg==msg)
{
Wea.hWnd=hWnd, Wea.lParam=lParam, Wea.wParam=wParam;
return (*Form1EventHandler[i].fnPtr)(Wea);
}
}
return (DefWindowProc(hWnd, msg, wParam, lParam));
}
Code:
//Form1.h //Needs to be included in Main.cpp because the WM_CREATE handler there
#ifndef FORM1_H //references fnForm1_WndProc as the Window Procedure for the Form1 Class.
#define FORM1_H //This would be needed to register the class.
LRESULT CALLBACK fnForm1_WndProc(HWND, unsigned int, WPARAM, LPARAM);
#endif
Code:
//Form2.cpp
#ifndef UNICODE
#define UNICODE
#endif
#ifndef _UNICODE
#define _UNICODE
#endif
#include <Windows.h>
#include <tchar.h>
#include "Main.h"
#include "Form2.h"
LRESULT fnForm2_OnCreate(WndEventArgs& Wea)
{
CREATESTRUCT* pCreateStruct;
HWND hMain;
pCreateStruct=(CREATESTRUCT*)Wea.lParam;
hMain=(HWND)pCreateStruct->lpCreateParams;
SetWindowLongPtr(Wea.hWnd,GWLP_USERDATA,(LONG_PTR)hMain);
ShowWindow(hMain,SW_HIDE);
return 0;
}
LRESULT fnForm2_OnPaint(WndEventArgs& Wea)
{
PAINTSTRUCT ps;
HDC hDC;
hDC=BeginPaint(Wea.hWnd,&ps);
TextOut(hDC,0,0,_T("This Is Form2. It SW_HIDEs The Main"),36);
TextOut(hDC,0,16,_T("Window, And SW_SHOWs It Upon Closing."),37);
TextOut(hDC,0,32,_T("This Technique Can Be Used Similiarly"),37);
TextOut(hDC,0,48,_T("To A Modal Dialog If It Isn't Necessary To"),42);
TextOut(hDC,0,64,_T("View Simultaneously A Form Underneath The"),41);
TextOut(hDC,0,80,_T("Dialog With Which You Can't Interact"),36);
TextOut(hDC,0,96,_T("Anyway"),6);
EndPaint(Wea.hWnd,&ps);
return 0;
}
LRESULT fnForm2_OnDestroy(WndEventArgs& Wea)
{
HWND hMain;
hMain=(HWND)GetWindowLongPtr(Wea.hWnd,GWLP_USERDATA);
EnableWindow(hMain,TRUE);
ShowWindow(hMain,TRUE);
return 0;
}
LRESULT CALLBACK fnForm2_WndProc(HWND hWnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
WndEventArgs Wea;
for(unsigned int i=0; i<dim(Form2EventHandler); i++)
{
if(Form2EventHandler[i].iMsg==msg)
{
Wea.hWnd=hWnd, Wea.lParam=lParam, Wea.wParam=wParam;
return (*Form2EventHandler[i].fnPtr)(Wea);
}
}
return (DefWindowProc(hWnd, msg, wParam, lParam));
}
Code:
//Form2.h //Needs to be included in Main.cpp because the WM_CREATE handler there
#ifndef FORM2_H //references fnForm2_WndProc as the Window Procedure for the Form1 Class.
#define FORM2_H //This would be needed to register the class.
LRESULT CALLBACK fnForm2_WndProc(HWND, unsigned int, WPARAM, LPARAM);
#endif
Code:
//Form3.cpp
#ifndef UNICODE
#define UNICODE
#endif
#ifndef _UNICODE
#define _UNICODE
#endif
#include <Windows.h>
#include <tchar.h>
#include "Main.h"
#include "Form3.h"
LRESULT fnForm3_OnPaint(WndEventArgs& Wea)
{
PAINTSTRUCT ps;
HDC hDC;
hDC=BeginPaint(Wea.hWnd,&ps);
TextOut(hDC,0,0,_T("This Is Form3. Not Only Does It Neither"),40);
TextOut(hDC,0,16,_T("Hide Nor Disable The Main Window, But"),37);
TextOut(hDC,0,32,_T("You'll Find That You Can Create As Many"),39);
TextOut(hDC,0,48,_T("Of These As You Like By Continually"),35);
TextOut(hDC,0,64,_T("Clicking The Bottom Button On The Main"),38);
TextOut(hDC,0,80,_T("Form. However, You'll Have To Drag One"),39);
TextOut(hDC,0,96,_T("From On Top Of The Other Because They"),37);
TextOut(hDC,0,112,_T("All Appear In The Same Location (I"),34);
TextOut(hDC,0,128,_T("Changed That). You May Further Note"),36);
TextOut(hDC,0,144,_T("That Since These Windows Are Neither"),36);
TextOut(hDC,0,160,_T("Disabled Nor Hidden At Any Time, You"),36);
TextOut(hDC,0,176,_T("May Interact With Them Irregardless Of"),38);
TextOut(hDC,0,192,_T("The State Of Form1 Or Form2. Pretty"),35);
TextOut(hDC,0,208,_T("Neat, Don't You Think?"),22);
EndPaint(Wea.hWnd,&ps);
return 0;
}
LRESULT fnForm3_OnClose(WndEventArgs& Wea)
{
HWND hMain;
MessageBox
(
Wea.hWnd,
_T("Good Way To Release Any Resources, Memory, etc., You May Have Allocated"),
_T("Window Close Report!"),
MB_OK
);
hMain=(HWND)GetWindowLongPtr(Wea.hWnd,GWLP_USERDATA);
EnableWindow(hMain,TRUE);
DestroyWindow(Wea.hWnd);
ShowWindow(hMain,TRUE);
return 0;
}
LRESULT CALLBACK fnForm3_WndProc(HWND hWnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
WndEventArgs Wea;
for(unsigned int i=0; i<dim(Form3EventHandler); i++)
{
if(Form3EventHandler[i].iMsg==msg)
{
Wea.hWnd=hWnd, Wea.lParam=lParam, Wea.wParam=wParam;
return (*Form3EventHandler[i].fnPtr)(Wea);
}
}
return (DefWindowProc(hWnd, msg, wParam, lParam));
}
Code:
//Form3.h //Needs to be included in Main.cpp because the WM_CREATE handler there
#ifndef FORM3_H //references fnForm3_WndProc as the Window Procedure for the Form1 Class.
#define FORM3_H //This would be needed to register the class.
LRESULT CALLBACK fnForm3_WndProc(HWND, unsigned int, WPARAM, LPARAM);
#endif
A worthwhile addition to the above program would be modifications to enable resolution independence at altered user DPI (dots per inch) settings.