16

I see plenty of tutorials and articles showing me how to make a simple windows program, which is great but none of them show me how to make multiple windows.

Right now I have working code that creates and draws a layered window and I can blit stuff using GDI to draw anything I want on it, drag it around, even make it transparent, etc.

But I wanted a second rectangular area that I can draw to, drag around, etc. In other words, a second window. Probably want it to be a child window. Question is, how do I make it?

Also, if anybody knows any good resources (online preferably) like articles or tutorials for window management in the Windows API, please share.

5 Answers 5

16

To create more than one window, repeat all the steps that you did when you created the first window to create a second window. A good way to do this is the copy and paste all the code from the first window. Then do search and replaces in which you replace all the names of the first window with unique names for the second window. The code in which I do just that is below.

The most important thing to note is that the windows class for the second window should have a unique name at the code line "windowclassforwindow2.lpszClassName="window class2". If it doesn't have a unique name, the windows registration will fail.

    #include <windows.h>

LRESULT CALLBACK windowprocessforwindow1(HWND handleforwindow1,UINT message,WPARAM wParam,LPARAM lParam);
LRESULT CALLBACK windowprocessforwindow2(HWND handleforwindow2,UINT message,WPARAM wParam,LPARAM lParam);

bool window1closed=false;
bool window2closed=false;

int WINAPI WinMain(HINSTANCE hInst,HINSTANCE hPrevInst,LPSTR lpCmdLine,int nShowCmd)
{
    bool endprogram=false;

    //create window 1

    WNDCLASSEX windowclassforwindow1;
    ZeroMemory(&windowclassforwindow1,sizeof(WNDCLASSEX));
    windowclassforwindow1.cbClsExtra=NULL;
    windowclassforwindow1.cbSize=sizeof(WNDCLASSEX);
    windowclassforwindow1.cbWndExtra=NULL;
    windowclassforwindow1.hbrBackground=(HBRUSH)COLOR_WINDOW;
    windowclassforwindow1.hCursor=LoadCursor(NULL,IDC_ARROW);
    windowclassforwindow1.hIcon=NULL;
    windowclassforwindow1.hIconSm=NULL;
    windowclassforwindow1.hInstance=hInst;
    windowclassforwindow1.lpfnWndProc=(WNDPROC)windowprocessforwindow1;
    windowclassforwindow1.lpszClassName=L"windowclass 1";
    windowclassforwindow1.lpszMenuName=NULL;
    windowclassforwindow1.style=CS_HREDRAW|CS_VREDRAW;

    if(!RegisterClassEx(&windowclassforwindow1))
    {
        int nResult=GetLastError();
        MessageBox(NULL,
            L"Window class creation failed",
            L"Window Class Failed",
            MB_ICONERROR);
    }

    HWND handleforwindow1=CreateWindowEx(NULL,
        windowclassforwindow1.lpszClassName,
            L"Parent Window",
            WS_OVERLAPPEDWINDOW,
            200,
            150,
            640,
            480,
            NULL,
            NULL,
            hInst,
            NULL                /* No Window Creation data */
);

    if(!handleforwindow1)
    {
        int nResult=GetLastError();

        MessageBox(NULL,
            L"Window creation failed",
            L"Window Creation Failed",
            MB_ICONERROR);
    }

    ShowWindow(handleforwindow1,nShowCmd);

    // create window 2

    WNDCLASSEX windowclassforwindow2;
    ZeroMemory(&windowclassforwindow2,sizeof(WNDCLASSEX));
    windowclassforwindow2.cbClsExtra=NULL;
    windowclassforwindow2.cbSize=sizeof(WNDCLASSEX);
    windowclassforwindow2.cbWndExtra=NULL;
    windowclassforwindow2.hbrBackground=(HBRUSH)COLOR_WINDOW;
    windowclassforwindow2.hCursor=LoadCursor(NULL,IDC_ARROW);
    windowclassforwindow2.hIcon=NULL;
    windowclassforwindow2.hIconSm=NULL;
    windowclassforwindow2.hInstance=hInst;
    windowclassforwindow2.lpfnWndProc=(WNDPROC)windowprocessforwindow2;
    windowclassforwindow2.lpszClassName=L"window class2";
    windowclassforwindow2.lpszMenuName=NULL;
    windowclassforwindow2.style=CS_HREDRAW|CS_VREDRAW;

    if(!RegisterClassEx(&windowclassforwindow2))
    {
        int nResult=GetLastError();
        MessageBox(NULL,
            L"Window class creation failed for window 2",
            L"Window Class Failed",
            MB_ICONERROR);
    }

    HWND handleforwindow2=CreateWindowEx(NULL,
        windowclassforwindow2.lpszClassName,
            L"Child Window",
            WS_OVERLAPPEDWINDOW,
            200,
            150,
            640,
            480,
            NULL,
            NULL,
            hInst,
            NULL);

    if(!handleforwindow2)
    {
        int nResult=GetLastError();

        MessageBox(NULL,
            L"Window creation failed",
            L"Window Creation Failed",
            MB_ICONERROR);
    }

    ShowWindow(handleforwindow2,nShowCmd);
    SetParent(handleforwindow2,handleforwindow1);
    MSG msg;
    ZeroMemory(&msg,sizeof(MSG));
    while (endprogram==false) {
        if (GetMessage(&msg,NULL,0,0));
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        if (window1closed==true && window2closed==true) {
            endprogram=true;
        }
    }
    MessageBox(NULL,
    L"Both Windows are closed.  Program will now close.",
    L"",
    MB_ICONINFORMATION);
    return 0;
}

LRESULT CALLBACK windowprocessforwindow1(HWND handleforwindow,UINT msg,WPARAM wParam,LPARAM lParam)
{
    switch(msg)
    {
        case WM_DESTROY: {
            MessageBox(NULL,
            L"Window 1 closed",
            L"Message",
            MB_ICONINFORMATION);

            window1closed=true;
            return 0;
        }
        break;
    }

    return DefWindowProc(handleforwindow,msg,wParam,lParam);
}

LRESULT CALLBACK windowprocessforwindow2(HWND handleforwindow,UINT msg,WPARAM wParam,LPARAM lParam)
{
    switch(msg)
    {
        case WM_DESTROY: {
            MessageBox(NULL,
            L"Window 2 closed",
            L"Message",
            MB_ICONINFORMATION);

            window2closed=true;
            return 0;
        }
        break;
    }

    return DefWindowProc(handleforwindow,msg,wParam,lParam);
}

A more complex example-using functions to create windows.

Creating each window without a function can be make the code cluttered-especially if it is in if statements. The code below uses a separate function to create each window. The first three windows have a create window button to create the next window.

    #include <Windows.h>

LRESULT CALLBACK windowprocessforwindow1(HWND handleforwindow1,UINT message,WPARAM wParam,LPARAM lParam);
LRESULT CALLBACK windowprocessforwindow2(HWND handleforwindow1,UINT message,WPARAM wParam,LPARAM lParam);
LRESULT CALLBACK windowprocessforwindow3(HWND handleforwindow1,UINT message,WPARAM wParam,LPARAM lParam);
LRESULT CALLBACK windowprocessforwindow4(HWND handleforwindow1,UINT message,WPARAM wParam,LPARAM lParam);

#define createwindowbuttoninwindow1 101
#define createwindowbuttoninwindow2 201
#define createwindowbuttoninwindow3 301

bool window1open,window2open,window3open,window4open=false;
bool windowclass1registeredbefore,windowclass2registeredbefore,
    windowclass3registeredbefore,windowclass4registeredbefore=false;

enum windowtoopenenumt {none,window2,window3,window4};

windowtoopenenumt windowtoopenenum=none;

void createwindow2(WNDCLASSEX& wc,HWND& hwnd,HINSTANCE hInst,int nShowCmd);
void createwindow3(WNDCLASSEX& wc,HWND& hwnd,HINSTANCE hInst,int nShowCmd);
void createwindow4(WNDCLASSEX& wc,HWND& hwnd,HINSTANCE hInst,int nShowCmd);

int WINAPI WinMain(HINSTANCE hInst,HINSTANCE hPrevInst,LPSTR lpCmdLine,int nShowCmd)
{
    bool endprogram=false;
    WNDCLASSEX windowclassforwindow2;   
    WNDCLASSEX windowclassforwindow3;
    WNDCLASSEX windowclassforwindow4;
    HWND handleforwindow2;
    HWND handleforwindow3;
    HWND handleforwindow4;

    //create window 1
    MSG msg;
    WNDCLASSEX windowclassforwindow1;
    ZeroMemory(&windowclassforwindow1,sizeof(WNDCLASSEX));
    windowclassforwindow1.cbClsExtra=NULL;
    windowclassforwindow1.cbSize=sizeof(WNDCLASSEX);
    windowclassforwindow1.cbWndExtra=NULL;
    windowclassforwindow1.hbrBackground=(HBRUSH)COLOR_WINDOW;
    windowclassforwindow1.hCursor=LoadCursor(NULL,IDC_ARROW);
    windowclassforwindow1.hIcon=NULL;
    windowclassforwindow1.hIconSm=NULL;
    windowclassforwindow1.hInstance=hInst;
    windowclassforwindow1.lpfnWndProc=(WNDPROC)windowprocessforwindow1;
    windowclassforwindow1.lpszClassName=L"window class 1";
    windowclassforwindow1.lpszMenuName=NULL;
    windowclassforwindow1.style=CS_HREDRAW|CS_VREDRAW;

    if(!RegisterClassEx(&windowclassforwindow1))
    {
        int nResult=GetLastError();
        MessageBox(NULL,
            L"Window class creation failed",
            L"Window Class Failed",
            MB_ICONERROR);
    }

    HWND handleforwindow1=CreateWindowEx(NULL,
            windowclassforwindow1.lpszClassName,
            L"Window 1",
            WS_OVERLAPPEDWINDOW,
            200,
            150,
            640,
            480,
            NULL,
            NULL,
            hInst,
            NULL                /* No Window Creation data */
);

    if(!handleforwindow1)
    {
        int nResult=GetLastError();

        MessageBox(NULL,
            L"Window creation failed",
            L"Window Creation Failed",
            MB_ICONERROR);
    }

    ShowWindow(handleforwindow1,nShowCmd);
    bool endloop=false;
    while (endloop==false) {
        if (GetMessage(&msg,NULL,0,0));
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        if (windowtoopenenum !=none) {
            switch (windowtoopenenum) {
                case window2:
                    if (window2open==false) {                                                       
                        createwindow2(windowclassforwindow2,handleforwindow2,hInst,nShowCmd);
                    }
                    break;
                case window3:
                    if (window3open==false) {           
                        createwindow3(windowclassforwindow3,handleforwindow3,hInst,nShowCmd);
                    }
                    break;
                case window4:               
                    if (window4open==false) {               
                        createwindow4(windowclassforwindow4,handleforwindow4,hInst,nShowCmd);
                    }
                    break;
            }
        windowtoopenenum=none;
    }
    if (window1open==false && window2open==false && window3open==false && window4open==false)
        endloop=true;

    }
    MessageBox(NULL,
            L"All Windows are closed.  Program will now close.",
            L"Message",
            MB_ICONINFORMATION);

}

void createwindow2(WNDCLASSEX& wc,HWND& hwnd,HINSTANCE hInst,int nShowCmd) {
    if (windowclass2registeredbefore==false) {
    ZeroMemory(&wc,sizeof(WNDCLASSEX));
    wc.cbClsExtra=NULL;
    wc.cbSize=sizeof(WNDCLASSEX);
    wc.cbWndExtra=NULL;
    wc.hbrBackground=(HBRUSH)COLOR_WINDOW;
    wc.hCursor=LoadCursor(NULL,IDC_ARROW);
    wc.hIcon=NULL;
    wc.hIconSm=NULL;
    wc.hInstance=hInst;
    wc.lpfnWndProc=(WNDPROC)windowprocessforwindow2;
    wc.lpszClassName=L"wc2";
    wc.lpszMenuName=NULL;
    wc.style=CS_HREDRAW|CS_VREDRAW;

    if(!RegisterClassEx(&wc))
    {
        int nResult=GetLastError();
        MessageBox(NULL,
            L"Window class creation failed",
            L"Window Class Failed",
            MB_ICONERROR);
    }
    else
        windowclass2registeredbefore=true;
    } 
    hwnd=CreateWindowEx(NULL,
            wc.lpszClassName,
            L"Window 2",
            WS_OVERLAPPEDWINDOW,
            200,
            170,
            640,
            480,
            NULL,
            NULL,
            hInst,
            NULL                /* No Window Creation data */
);

    if(!hwnd)
    {
        int nResult=GetLastError();

        MessageBox(NULL,
            L"Window creation failed",
            L"Window Creation Failed",
            MB_ICONERROR);
    }

    ShowWindow(hwnd,nShowCmd);
}

void createwindow3(WNDCLASSEX& wc,HWND& hwnd,HINSTANCE hInst,int nShowCmd) {
    if (windowclass3registeredbefore==false) {
    ZeroMemory(&wc,sizeof(WNDCLASSEX));
    wc.cbClsExtra=NULL;
    wc.cbSize=sizeof(WNDCLASSEX);
    wc.cbWndExtra=NULL;
    wc.hbrBackground=(HBRUSH)COLOR_WINDOW;
    wc.hCursor=LoadCursor(NULL,IDC_ARROW);
    wc.hIcon=NULL;
    wc.hIconSm=NULL;
    wc.hInstance=hInst;
    wc.lpfnWndProc=(WNDPROC)windowprocessforwindow3;
    wc.lpszClassName=L"window class 3";
    wc.lpszMenuName=NULL;
    wc.style=CS_HREDRAW|CS_VREDRAW;

    if(!RegisterClassEx(&wc))
    {
        int nResult=GetLastError();
        MessageBox(NULL,
            L"Window class creation failed",
            L"Window Class Failed",
            MB_ICONERROR);
    }
    else
        windowclass3registeredbefore=true;
    }
    hwnd=CreateWindowEx(NULL,
            wc.lpszClassName,
            L"Window 3",
            WS_OVERLAPPEDWINDOW,
            200,
            190,
            640,
            480,
            NULL,
            NULL,
            hInst,
            NULL                /* No Window Creation data */
);

    if(!hwnd)
    {
        int nResult=GetLastError();

        MessageBox(NULL,
            L"Window creation failed",
            L"Window Creation Failed",
            MB_ICONERROR);
    }

    ShowWindow(hwnd,nShowCmd);
}

void createwindow4(WNDCLASSEX& wc,HWND& hwnd,HINSTANCE hInst,int nShowCmd) {
    if (windowclass4registeredbefore==false) {
        ZeroMemory(&wc,sizeof(WNDCLASSEX));
    wc.cbClsExtra=NULL;
    wc.cbSize=sizeof(WNDCLASSEX);
    wc.cbWndExtra=NULL;
    wc.hbrBackground=(HBRUSH)COLOR_WINDOW;
    wc.hCursor=LoadCursor(NULL,IDC_ARROW);
    wc.hIcon=NULL;
    wc.hIconSm=NULL;
    wc.hInstance=hInst;
    wc.lpfnWndProc=(WNDPROC)windowprocessforwindow4;
    wc.lpszClassName=L"window class 4";
    wc.lpszMenuName=NULL;
    wc.style=CS_HREDRAW|CS_VREDRAW;

    if(!RegisterClassEx(&wc))
    {
        int nResult=GetLastError();
        MessageBox(NULL,
            L"Window class creation failed",
            L"Window Class Failed",
            MB_ICONERROR);
    }
    else
        windowclass4registeredbefore=true;
    }
    hwnd=CreateWindowEx(NULL,
            wc.lpszClassName,
            L"Window 4",
            WS_OVERLAPPEDWINDOW,
            200,
            210,
            640,
            480,
            NULL,
            NULL,
            hInst,
            NULL                /* No Window Creation data */
);

    if(!hwnd)
    {
        int nResult=GetLastError();

        MessageBox(NULL,
            L"Window creation failed",
            L"Window Creation Failed",
            MB_ICONERROR);
    }

    ShowWindow(hwnd,nShowCmd);
}

// windows process functions

LRESULT CALLBACK windowprocessforwindow1(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam) {
    switch(message) {
        case WM_CREATE:
                window1open=true;
                CreateWindowEx(NULL,
                L"BUTTON",
                L"Open Window 2",
                WS_TABSTOP|WS_VISIBLE|
                WS_CHILD|BS_DEFPUSHBUTTON,
                50,
                220,
                150,
                24,
                hwnd,
                (HMENU)createwindowbuttoninwindow1,
                GetModuleHandle(NULL),
                NULL);
            break;
            case WM_DESTROY:
                window1open=false;
                break;
        case WM_COMMAND:
            switch LOWORD(wParam) {
                case createwindowbuttoninwindow1:
                    windowtoopenenum=window2;
                    break;
            }
    }
    return DefWindowProc(hwnd, message, wParam, lParam);

}

LRESULT CALLBACK windowprocessforwindow2(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam) {
    switch(message) {
        case WM_CREATE:
                window2open=true;
                CreateWindowEx(NULL,
                L"BUTTON",
                L"Open Window 3",
                WS_TABSTOP|WS_VISIBLE|
                WS_CHILD|BS_DEFPUSHBUTTON,
                50,
                220,
                150,
                24,
                hwnd,
                (HMENU)createwindowbuttoninwindow2,
                GetModuleHandle(NULL),
                NULL);
            break;
            case WM_DESTROY:
                window2open=false;
                break;
        case WM_COMMAND:
            switch LOWORD(wParam) {
                case createwindowbuttoninwindow2:
                    windowtoopenenum=window3;
                    break;
            }
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

LRESULT CALLBACK windowprocessforwindow3(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam) {
    switch(message) {
        case WM_CREATE:
                window3open=true;
                CreateWindowEx(NULL,
                L"BUTTON",
                L"Open Window 4",
                WS_TABSTOP|WS_VISIBLE|
                WS_CHILD|BS_DEFPUSHBUTTON,
                50,
                220,
                150,
                24,
                hwnd,
                (HMENU)createwindowbuttoninwindow3,
                GetModuleHandle(NULL),
                NULL);
                break;
                case WM_DESTROY:
                window3open=false;
                break;
        case WM_COMMAND:
            switch LOWORD(wParam) {
                case createwindowbuttoninwindow3:
                    windowtoopenenum=window4;
                    break;
            }
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

LRESULT CALLBACK windowprocessforwindow4(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam) {
    switch(message) {
        case WM_DESTROY:
            window4open=false;
            break;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

What if you close and reopen a window?

If you click on the close button and reopen that same window, note the following. When the window is closed after the close button is closed, it will be destroyed. But destroying a window does not destroy the windows class. It only destroys the window from the createwindow function. This makes it necessary for the if statement in the above program that only creates the Windows class if it is the first time the window has been displayed.

Some side notes

You could create multiple windows using just one windows class. But the problem with that is that you have one windows process function to deal with more than one window. That would work fine in this simple example. But the more heterogeneous the windows are the more would be the need to create a separate windows class for each window.

Also the multiple createwindow functions could have been combined into one function. Note that the only difference between them was the wc.lpszClassName code line. But Windows are likely to be different from each other so combining the functions into one is not necessary-it is more of a preference of just not having code repeating things.

Further Reading

The link at the website with the domain functionx has more details about the concepts in windows design. The link is here

The home page at functionx.com has good programming learning resources. Especially important is this page which has programming reference material for things such as changing the windows class, creating listboxes and other windows controls. It is also a good resource for win32 programming learning in general. functionx.com win32 programming

functionx.com win32 programming

Sign up to request clarification or add additional context in comments.

Comments

13

You can hit CreateWindow() more than once if you want. The message loop in your WinMain will pass events to all the windows that WinMain creates. You can even create two overlapped windows and set the parent window of the 2nd one to be the handle of the 1st one if you want.

4 Comments

I got it working, finally. It worked when I modified my routine that creates windows by allowing it to register a window class using a different ClassName string. Why would I need to make a separate window class for the second window if the functionality should stay the same?
@Steven Lu: not sure. The test I ran to double check only registered one window class and was able to CreateWindow twice. Of course, this means they share a WndProc. You don't have static data inside the WndProc, do you?
I was still using the same window proc, but it had to be a different window class, i.e. I had to call RegisterClassEx with a different WNDCLASSEX::lpszClassName. Is this normal?
@Steven Lu - no, you should be able to create several instances of the same class, unless there's something about the class that is static so that the different instances are stepping on one another.
3

It sounds like you want a Multiple Document Interface. Here is an example of one:

http://www.codeproject.com/KB/winsdk/Sigma.aspx

Comments

3

I know this has already been answered, but I was just writing a program that opens an arbitrary number of windows via a for loop.

Here is my version. Basically, it reuses the same class generator to build multiple windows. You can create as many as you want. Just make sure to adjust your HWND[] and WNDCLASSEX wc[] arrays accordingly.

Note 1: This piece of code uses a global ApplicationInstance, which is derived from WinMain function. In your WinMain, assign received hInstance to ApplicationInstance which, in this example is assumed to be available globally. This is your primary application window instance.

Note 2: Of course, you have to have WinProc routine already pre-written, and included somewhere in another header file, or just above (not shown in this example.) In this code, it is referenced to as WinProc, when it's passed to PopulateClass(WNDPROC process)

Note 3: SpawnWindow supports "centered" and "maximized" flags. What they do is self-explanatory.

Also, window class name is auto-generated, so you never have to worry about naming it, just assign it a good base name.

int WindowCounter = 0;
WNDCLASSEX wc[1000];
HWND hwnd[1000];
char class_name[256]; // for auto class name generation

void PopulateClass(WNDPROC process) {
    ZeroMemory(&wc[WindowCounter], sizeof(WNDCLASSEX));
    wc[WindowCounter].cbSize = sizeof(WNDCLASSEX);
    wc[WindowCounter].style = CS_HREDRAW|CS_VREDRAW|CS_OWNDC;
    wc[WindowCounter].lpfnWndProc = process;
    wc[WindowCounter].cbClsExtra = 0;
    wc[WindowCounter].cbWndExtra = 0;
    wc[WindowCounter].hInstance = ApplicationInstance;
    wc[WindowCounter].hIcon = LoadIcon(nullptr, IDI_APPLICATION);
    wc[WindowCounter].hCursor = LoadCursor(nullptr, IDC_ARROW);
    wc[WindowCounter].hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
    wc[WindowCounter].lpszMenuName = nullptr;
    sprintf(class_name, "WindowClass%d", WindowCounter);
    wc[WindowCounter].lpszClassName = class_name;
    wc[WindowCounter].hIconSm = nullptr;
}

Now, let's put it all together by providing a SpawnWindow function!

HWND SpawnWindow(int x,
                 int y,
                 int width,
                 int height,
                 bool centered = false,
                 bool maximized = false) {
    PopulateClass(WinProc);
    RegisterClassEx(&wc[ WindowCounter ]);
    int config_style = WS_OVERLAPPEDWINDOW;
    if (maximized) { width = GetSystemMetrics(SM_CXFULLSCREEN); height =     GetSystemMetrics(SM_CYFULLSCREEN); config_style = WS_OVERLAPPEDWINDOW |     WS_MAXIMIZE;  }
    if (centered) { x = (GetSystemMetrics(SM_CXFULLSCREEN) / 2) - (width /     2); y = (GetSystemMetrics(SM_CYFULLSCREEN) / 2) - (height / 2); }
    hwnd[WindowCounter] = CreateWindowEx(NULL,
        wc[WindowCounter].lpszClassName,
        config.namever(),
        WS_OVERLAPPEDWINDOW,
        x,
        y,
        width,
        height,
        nullptr,
        nullptr,
        ApplicationInstance,
        nullptr);
    HWND returnID = hwnd[WindowCounter];
    ShowWindow(hwnd[WindowCounter++], SW_SHOW);
    return returnID;
}

Finally, create as many windows as you want with just a single line of code each:

void CreateWindows() {
    HWND PrimaryWindow1 = SpawnWindow(500, 500, 250, 250);
    HWND PrimaryWindow2 = SpawnWindow(500, 500, 250, 250, true);
    HWND PrimaryWindow3 = SpawnWindow(500, 500, 250, 250, true, true);
    HWND PrimaryWindow4 = SpawnWindow(100, 100, 150, 150);
    HWND PrimaryWindow5 = SpawnWindow(450, 500, 350, 150);
}

Call CreateWindows() from your WinMain, before entering main loop.

Hope this helps someone out there.

Using a single WinProc for all windows

Note, this will require additional modification to the above code. In particular, passing a custom class name to the SpawnWindow function, representing each window. For example: "WindowClass_App", where "WindowClass_" can be a static part of the name, and actual identifier would be just: "App", "Toolbox", "Sidebar", "AnotherCustomWindow", etc. First define them:

#define WindowClass_App                    0
#define WindowClass_Layers                 2
#define WindowClass_OpenGL                 3
/* ...etc... */

I wrote a function that converts a string to an int ID. This ID will be used to branch out within the single WinProc function, based on which window class the message has been received:

int IdentifyWindowClassID(const char *lpClassName) {
    int WindowClassID = -1;
    // Convert string names to integers, because C++ switch does not support strings
    if (strcmp(lpClassName, "WindowClass_App") == 0) WindowClassID = WindowClass_App;
    if (strcmp(lpClassName, "WindowClass_Layers") == 0) WindowClassID = WindowClass_Layers;
    if (strcmp(lpClassName, "WindowClass_OpenGL") == 0) WindowClassID = WindowClass_OpenGL;
    /* etc */
    return WindowClassID;
}

And finally, the WinProc itself:

long __stdcall WinProc(HWND hwnd, unsigned int msg, WPARAM wparam, LPARAM lparam)
{
    char lpClassName[128];
    GetClassName(hwnd, lpClassName, 128);

    int WindowClassID = IdentifyWindowClassID( lpClassName );

    switch (WindowClassID)
    {
        /* 1.) Main application window */
        case WindowClass_App: { 
            switch (msg) {    
                case WM_CREATE: {
                    /* ...code... */
                }
            /* ...code... */
        }

        /* 2.) Layers window */
        case WindowClass_Layers: {
            switch (msg) {    
                case WM_CREATE: {
                    /* ...code... */
                }
            /* ...code... */
        }

        /* 3.) OpenGL view window... */

       ...

This is the basic pattern. Of course, you can craft it in any way you want, this is just how I did it, it's simple and it works for me.

3 Comments

I like your solution to multiple windows! I have a couple of questions: 1. Does one WinProc function handle all the request from all the windows or does this end up actually using a WinProc function for each window? 2. What if I want to call SpawnWindow(…) sometime after WinMain has completed in some function that receives commands from another application - how could I do that?
1.) I updated the answer with a bit more code to show how I do a single WinProc. For question 2.) WinMain usually doesn't "complete" until the main application loop is broken (which is basically synonymous with "Quit".) But my guess is, you are speaking of events received from other applications? (Like, drag a file into the app, for example?) Well, look into Window messages. There is one for pretty much everything. They are all handled by the WinProc. Hence, when you say "receives commands", if it's possible there is a WM_* of some sorts for that action.
Thanks InfiniteStack for the updated code - this is very helpful!
2

You can create as many windows as you want using CreateWindow/CreateWindowEx, with the relationship between them as you desire (owner/child).

You can make a window "owned" by other with:

SetWindowLongPtr(hwnd, GWLP_HWNDPARENT, (LONG_PTR) hwndParent);

To convert a window to child, use SetParent.

Note that the SetWindowLongPtr call with GWLP_HWNDPARENT does not behave as SetParent (MSDN is wrong on this I think). GWLP_HWNDPARENT does not convert a window to "child", but to "owned".

1 Comment

You create an owner/owned relationship by passing the owner window handle to the CreateWindow[Ex] call (hWndParent parameter). If the WS_CHILD window style is not present, hWndParent will be the owner. If it is, hWndParent will designate the parent window, and a parent/child relationship is established instead.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.