Win32 API - Having more than one instance of a window

Hello guys,
I have been using C++ for half a year now. Until now I have always used Java for graphical things.

I have learned how to create a window, rename it, create a menu with resources, create text boxes, buttons and make them do something.
My problem is that I cannot figure out how to create more than one instance of a window. And with another instance of a window I mean a fully blown up version, with its own menu, buttons, etc. I tried to google this but I found no result. So can anyone tell me how to do this? I am using Visual Studio 2013 and C++, not C.
By the way, does somebody knows a good tutorial for 2D graphics and API for C++?
Note: While I know how to program in C++, I have never used C as I am too young for the C era. From what I know, a C++ compiler should be able to run a C program. I know that C lacks all the object-oriented features of C++. Apart from that I am not aware of the exact functionality differences.

This is Visual Studio 2013's standard code for a win32 program + some additions for learning how buttons and text boxes work.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
 
// Win32Project3.cpp : Defines the entry point for the application.
//
#include <windows.h>
#include "stdafx.h"
#include "Win32Project3.h"
#include <iostream>
using namespace std;
#include <string>

#define MAX_LOADSTRING 100
#define ID_TEXTBOX 1
#define ID_BUTTON 2
#define ID_BUTTONnew 3
#define ID_BUTTONnewWindow 4

// Global Variables:
HINSTANCE hInst;								// current instance
TCHAR szTitle[MAX_LOADSTRING];					// The title bar text
TCHAR szWindowClass[MAX_LOADSTRING];			// the main window class name
//Custom Variables
static HWND hWndTextBox;

// Forward declarations of functions included in this code module:
ATOM			MyRegisterClass(HINSTANCE hInstance);
BOOL			InitInstance(HINSTANCE, int);
LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK	About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPTSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);

 	// TODO: Place code here.
	MSG msg;
	HACCEL hAccelTable;

	// Initialize global strings
	LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
	LoadString(hInstance, IDC_WIN32PROJECT3, szWindowClass, MAX_LOADSTRING);
	MyRegisterClass(hInstance);

	// Perform application initialization:
	if (!InitInstance (hInstance, nCmdShow))
	{
		return FALSE;
	}

	hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WIN32PROJECT3));

	// Main message loop:
	while (GetMessage(&msg, NULL, 0, 0))
	{
		if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	return (int) msg.wParam;
}



//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX);

	wcex.style		= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra	= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon		= LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32PROJECT3));
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= MAKEINTRESOURCE(IDC_WIN32PROJECT3);
	wcex.lpszClassName	= szWindowClass;
	wcex.hIconSm		= LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

	return RegisterClassEx(&wcex);
}

//
//   FUNCTION: InitInstance(HINSTANCE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance; // Store instance handle in our global variable

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for the main window.
//
//  WM_COMMAND	- process the application menu
//  WM_PAINT	        - Paint the main window
//  WM_DESTROY	- post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	PAINTSTRUCT ps;
	HDC hdc;

	switch (message)
	{
	case WM_CREATE:
	{
					  hWndTextBox = CreateWindow(TEXT("EDIT"), TEXT("value"),
						  WS_VISIBLE | WS_CHILD | WS_BORDER | ES_AUTOHSCROLL,
						  10, 10, 200, 20, hWnd, (HMENU)ID_TEXTBOX, NULL, NULL);
					  CreateWindow(TEXT("button"), TEXT("Click Me"),
						  WS_VISIBLE | WS_CHILD,
						  10, 40, 80, 25,
						  hWnd, (HMENU) ID_BUTTON, NULL, NULL);

					  CreateWindow(TEXT("BUTTON"), TEXT("button"),
						  WS_CHILD | WS_VISIBLE,
						  10, 80, 80, 25,
						  hWnd, (HMENU)ID_BUTTONnew, NULL, NULL);

					  CreateWindow(TEXT("BUTTON"), TEXT("new window"),
						  WS_CHILD | WS_VISIBLE,
						  10, 120, 90, 25,
						  hWnd, (HMENU)ID_BUTTONnewWindow, NULL, NULL);
					  break;
	}
	case WM_COMMAND:
		wmId    = LOWORD(wParam);
		wmEvent = HIWORD(wParam);
		//Actions of the window elements like text boxes and buttons
		//Actions for the default button
		if (LOWORD(wParam) == ID_BUTTON) {
			//create some default variables
			int length = GetWindowTextLength(hWndTextBox) + 1;
			static char title[500];

			GetWindowTextA(hWndTextBox, title, length);

			SetWindowTextA(hWnd, title);
			//MessageBoxA(hWnd, title, "Message box", MB_OK);
			break;
		}
			//Actions of the new button
			if (LOWORD(wParam) == ID_BUTTONnew) {
				MessageBoxA(hWnd, "Button has been clicked", "title for popup",
					MB_ICONINFORMATION);

			break;
		}
			//Open another instance of an window
			if (LOWORD(wParam) == ID_BUTTONnewWindow) {

				break;
			}

		// Parse the menu selections:
		switch (wmId)
		{
		case IDM_ABOUT:
			DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
			break;
		case IDM_EXIT:
			DestroyWindow(hWnd);
			break;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
		}
		break;
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		// TODO: Add any drawing code here...
		EndPaint(hWnd, &ps);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);
	switch (message)
	{
	case WM_INITDIALOG:
		return (INT_PTR)TRUE;

	case WM_COMMAND:
		if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
		{
			EndDialog(hDlg, LOWORD(wParam));
			return (INT_PTR)TRUE;
		}
		break;
	}
	return (INT_PTR)FALSE;
}

Last edited on
My problem is that I cannot figure out how to create more than one instance of a window


just call CreateWindow as many times as you want
Last edited on
tath said:
just call CreateWindow as many times as you want

Just remember to save the return value to different variables.
As I am really new with Win32 API, could anyone please post an example code, how I could complete this? I have already tried this, but I always get some syntax error. The part where the new Window is created would be enough. It doesn't matter which style is applied or where it is located.
Thanks in advane.
Here is proof of concept example - cloning window :0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
//multiple windows test by tath
#include <windows.h>

// defines
#define BUTTON_ID (HMENU)100
#define CLOSE_BUTTON_ID (HMENU)101

// local memory
static int gX = 0, gY = 0; // new window position
static int gNumberOfWindows = 0; // close application

// prototypes
LRESULT CALLBACK WinProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

// create window
static BOOL registerMainWindow(HINSTANCE hInst)
{
    WNDCLASSEX wClass = { 0 };
    BOOL fRet = TRUE;

    wClass.cbClsExtra = 0;
    wClass.cbSize = sizeof(WNDCLASSEX);
    wClass.cbWndExtra = 0;
    wClass.hbrBackground = (HBRUSH)COLOR_WINDOW;
    wClass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wClass.hIcon = 0;
    wClass.hIconSm = 0;
    wClass.hInstance = hInst;
    wClass.lpfnWndProc = (WNDPROC)WinProc;
    wClass.lpszClassName = L"Window Class";
    wClass.lpszMenuName = 0;
    wClass.style = 0;

    if (0 == RegisterClassEx(&wClass))
    {
        MessageBox(NULL,
            L"RegisterClassEx failed",
            L"Window Class Failed",
            MB_ICONERROR);

        fRet = FALSE;
    }

    return fRet;
}

static HANDLE  createWindow(HINSTANCE hInst, int x, int y)
{
    HANDLE hWinHandle = 0;

    hWinHandle = CreateWindowEx(0,
        L"Window Class",
        L"multiple windows test by tath",
        WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
        x,
        y,
        200,
        200,
        NULL,
        NULL,
        hInst,
        NULL);

    if (NULL == hWinHandle)
    {
        MessageBox(NULL,
            L"CreateWindowEx failed",
            L"Window Creation Failed",
            MB_ICONERROR);
    }
    else
    {
        gNumberOfWindows++;
    }

    return hWinHandle;
}

// main
int WINAPI WinMain(
    _In_        HINSTANCE hInst,
    _In_opt_    HINSTANCE hPrevInst,
    _In_        LPSTR lpCmdLine,
    _In_        int nShowCmd)
{
    MSG     msg = { 0 };
    HANDLE hFirstWindow = 0;
    BOOL fInit = FALSE;
    
    fInit = registerMainWindow(hInst);

    if (FALSE != fInit)
    {
        gX = 200;
        gY = 200;

        hFirstWindow = createWindow(hInst, gX, gY);

        if (hFirstWindow)
        {
            ShowWindow(hFirstWindow, SW_SHOWNORMAL);

            while (FALSE != GetMessage(&msg, NULL, 0, 0))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }
    }

    return 0;
}

// callbacks
static LRESULT onCreate(HWND hParent)
{
    HANDLE hButton = 0;
    // in this example, all window buttons has the same Id.
    // also handle is not needed outside of this scope since buttons
    // are binded to parent window. When parent is destroyed it will
    // destroy its children (and all other system objects)
    hButton = CreateWindowEx(0, L"BUTTON", L"new window",
        WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
        10, 10, 100, 50, hParent, BUTTON_ID,
        GetModuleHandle(NULL), 0);

    if (0 == hButton)
    {
        MessageBox(NULL,
            L"CreateWindowEx failed",
            L"button Creation Failed",
            MB_ICONERROR);
    }

    hButton = CreateWindowEx(0, L"BUTTON", L"close all",
        WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
        10, 70, 100, 50, hParent, CLOSE_BUTTON_ID,
        GetModuleHandle(NULL), 0);

    if (0 == hButton)
    {
        MessageBox(NULL,
            L"CreateWindowEx failed",
            L"button Creation Failed",
            MB_ICONERROR);
    }

    return 0;
}

static LRESULT onDestroy()
{
    gNumberOfWindows--;

    if (gNumberOfWindows <= 0)
    {
        PostQuitMessage(0);
    }

    return 0;
}

static LRESULT onButtonPress(HWND hParent)
{
    HANDLE hNewWindow = 0;
    gX += 20;
    gY += 20;

    hNewWindow = createWindow(GetModuleHandle(NULL), gX, gY);

    if (hNewWindow)
    {
        ShowWindow(hNewWindow, SW_SHOWNORMAL);
    }

    return 0;
}

LRESULT CALLBACK WinProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_CREATE:
        return onCreate(hWnd);
        break;
    case WM_DESTROY:
        return onDestroy();
        break;
    case WM_COMMAND:
        {
            switch (LOWORD(wParam))
            {
            case BUTTON_ID:
                return onButtonPress(hWnd);
                break;
            case CLOSE_BUTTON_ID:
                PostQuitMessage(0);
                return 0;
                break;
            }
        }
        break;
    }

    return DefWindowProc(hWnd, msg, wParam, lParam);
}
Can't get much shorter or easier than this ..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <windows.h>

LRESULT CALLBACK WndProc(HWND hWnd, unsigned int wMsg, WPARAM wParam, LPARAM lParam)
{
 if(wMsg==WM_DESTROY)
 {
    PostQuitMessage(0);
    return 0;
 }

 return (DefWindowProc(hWnd, wMsg, wParam, lParam));
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
 char szClassName[]="Form1A";
 WNDCLASSEX wc{0};
 MSG msg;
 HWND hWnd;

 wc.lpszClassName=szClassName;                wc.lpfnWndProc=WndProc;
 wc.cbSize=sizeof (WNDCLASSEX);               wc.hInstance=hInstance;
 RegisterClassEx(&wc);
 for(int i=0; i<4; i++)
 {
     hWnd=CreateWindowEx(0,szClassName,szClassName,WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,320,305,HWND_DESKTOP,0,hInstance,0);
     ShowWindow(hWnd,iShow);
 }
 while(GetMessage(&msg,NULL,0,0))
 {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
 }

 return msg.wParam;
}


Actually, that's a pretty poor program, but it does create four main program windows. Only trouble is, when you click the [x] to close any one, they all get closed because PostQuitMessage() is called and that kills the whole process. To do a better job of it you could save the count of windows in the WNDCLASSEX::cbClsExtra bytes, decrement the count when a window is destroyed, and call PostQuitMessage() when the count reaches zero. But that's extra credit!

Realize that when you've Registered a Window Class, the specified Window Procedure applies to ALL windows of that class that are created. These multiple windows created by multiple CreateWindowEx() function calls all use the same Window Procedure and are differentiated in the Window Procedure by the HWND parameter.

Also note the above code won't work immediately in Visual Studio because I used single byte characters. You would need to change the character set to unspecified or non-wide character. Either that or change the char to wchar_t. Should compile 'as is' with CodeBlocks though.
Thank you very much. I was able to to create a second window using the concept of your example code. As I am very busy this week, I will work on the closing problem sometime later.
I think I have found a tutorial that will solve my problem:
http://www.winprog.org/tutorial/app_four.html (App Part 4: Multiple Document Interface).

I haven't implemented a code using the tutorial yet, but it should let me do what I want. The windows created are only child windows, but that won't matter for the time being as this is OK with my next two or three programs. After having created them, I should have enough experience to figure out the rest with the help of Microsoft's documentation.

I really appreciate the help you have given me, especially the code which was quite useful.
That's the tutorial that's most often recommended around here. Note though that its old, and uses chars instead of TCHARs or wchar_ts.

The MDI part of that tutorial is particularly good. Also, Charles Petzold's book has a good MDI program (the files can be downloaded from hos site).
Topic archived. No new replies allowed.