【问题标题】:How to make multiple windows using Win32 API如何使用 Win32 API 制作多个窗口
【发布时间】:2011-02-22 14:48:34
【问题描述】:

我看到很多教程和文章教我如何制作一个简单的 windows 程序,这很棒,但没有一篇教我如何制作多个 windows。

现在我有创建和绘制分层窗口的工作代码,我可以使用 GDI 在上面绘制任何我想要的东西,拖动它,甚至让它透明等等。

但我想要第二个矩形区域,我可以绘制、拖动等。换句话说,第二个窗口。可能希望它是一个子窗口。问题是,我该怎么做?

另外,如果有人知道任何好的资源(最好是在线资源),例如 Windows API 中的窗口管理文章或教程,请分享。

【问题讨论】:

    标签: c++ windows winapi


    【解决方案1】:

    要创建多个窗口,请重复创建第一个窗口时执行的所有步骤以创建第二个窗口。一个很好的方法是从第一个窗口复制并粘贴所有代码。然后进行搜索和替换,将第一个窗口的所有名称替换为第二个窗口的唯一名称。我这样做的代码如下。

    最需要注意的是第二个窗口的windows类在代码中应该有一个唯一的名字 "windowclassforwindow2.lpszClassName="window class2" 行。如果没有唯一的名称,windows 注册将失败。

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

    一个更复杂的例子——使用函数创建窗口。

    在没有函数的情况下创建每个窗口会使代码混乱——尤其是如果它在 if 语句中。下面的代码使用一个单独的函数来创建每个窗口。前三个窗口有一个创建窗口按钮,用于创建下一个窗口。

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

    如果您关闭并重新打开一个窗口会怎样?

    如果您单击关闭按钮并重新打开同一窗口,请注意以下事项。当关闭按钮关闭后窗口关闭时,它会被销毁。但是破坏一个窗口并不会破坏 windows 类。它只会从 createwindow 函数中销毁窗口。这使得上述程序中的 if 语句必须在第一次显示窗口时才创建 Windows 类。

    一些旁注

    您可以只使用一个窗口类来创建多个窗口。但这样做的问题是你有一个窗口进程函数来处理多个窗口。在这个简单的例子中可以正常工作。但是窗口越异构,就越需要为每个窗口创建一个单独的窗口类。

    多个 createwindow 函数也可以合并为一个函数。请注意,它们之间的唯一区别是 wc.lpszClassName 代码行。但是 Windows 可能彼此不同,因此没有必要将这些功能组合成一个 - 它更多的是不让代码重复事物。

    进一步阅读

    网站上带有域 functionx 的链接有更多关于 windows 设计概念的详细信息。链接是here

    functionx.com 的主页有很好的编程学习资源。 尤其重要的是这个页面,它包含诸如更改窗口类、创建列表框和其他窗口控件之类的编程参考资料。总的来说,它也是一个很好的 win32 编程学习资源。 functionx.com win32编程

    functionx.com win32 programming

    【讨论】:

      【解决方案2】:

      如果需要,您可以多次点击 CreateWindow()。 WinMain 中的消息循环会将事件传递给 WinMain 创建的所有窗口。如果需要,您甚至可以创建两个重叠的窗口,并将第二个的父窗口设置为第一个的句柄。

      【讨论】:

      • 我终于搞定了。当我通过允许它使用不同的 ClassName 字符串注册窗口类来修改创建窗口的例程时,它起作用了。如果功能应该保持不变,为什么我需要为第二个窗口创建一个单独的窗口类?
      • @Steven Lu:不确定。我进行仔细检查的测试只注册了一个窗口类,并且能够两次 CreateWindow。当然,这意味着它们共享一个 WndProc。 WndProc 中没有静态数据,是吗?
      • 我仍在使用相同的窗口 proc,但它必须是不同的窗口类,即我必须使用不同的 WNDCLASSEX::lpszClassName 调用 RegisterClassEx。这正常吗?
      • @Steven Lu - 不,您应该能够创建同一个类的多个实例,除非该类的某些东西是静态的,因此不同的实例会相互影响。
      【解决方案3】:

      【讨论】:

        【解决方案4】:

        我知道这个问题已经得到解答,但我只是在编写一个通过 for 循环打开任意数量窗口的程序。

        这是我的版本。基本上,它重用同一个类生成器来构建多个窗口。您可以根据需要创建任意数量。只需确保相应地调整 HWND[] 和 WNDCLASSEX wc[] 数组即可。

        注意 1:这段代码使用了一个全局的 ApplicationInstance,它是从 WinMain 函数派生的。在您的 WinMain 中,将收到的 hInstance 分配给 ApplicationInstance,在此示例中假定它是全局可用的。这是您的主要应用程序窗口实例。

        注意 2:当然,您必须预先编写 WinProc 例程,并包含在另一个头文件中的某个位置,或者就在上面(本示例中未显示)。在此代码中,它被称为 WinProc,当它被传递给 PopulateClass(WNDPROC 进程)

        注意 3:SpawnWindow 支持“居中”和“最大化”标志。他们所做的事情是不言自明的。

        此外,窗口类名称是自动生成的,因此您不必担心命名,只需为其指定一个好的基本名称即可。

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

        现在,让我们通过提供 SpawnWindow 函数将它们放在一起!

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

        最后,只需一行代码即可创建任意数量的窗口:

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

        在进入主循环之前,从 WinMain 调用 CreateWindows()。

        希望这对那里的人有所帮助。

        对所有窗口使用一个 WinProc

        注意,这需要对上述代码进行额外修改。特别是,将自定义类名传递给 SpawnWindow 函数,表示每个窗口。例如:“WindowClass_App”,其中“WindowClass_”可以是名称的静态部分,实际标识符只是:“App”、“Toolbox”、“Sidebar”、“AnotherCustomWindow”等。首先定义它们:

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

        我编写了一个将字符串转换为 int ID 的函数。此 ID 将用于在单个 WinProc 函数中分支,具体取决于已收到消息的窗口类:

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

        最后,WinProc 本身:

        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... */
        
               ...
        

        这是基本模式。当然,你可以用任何你想要的方式来制作它,我就是这样做的,它很简单,而且对我有用。

        【讨论】:

        • 我喜欢你的多窗口解决方案!我有几个问题: 1. 一个 WinProc 函数是否处理来自所有窗口的所有请求,还是最终实际上为每个窗口使用了一个 WinProc 函数? 2. 如果我想在 WinMain 完成某个从另一个应用程序接收命令的函数后调用 SpawnWindow(...) 怎么办?
        • 1.) 我用更多代码更新了答案,以展示我如何执行单个 WinProc。对于问题 2。)WinMain 通常在主应用程序循环被破坏之前不会“完成”(这基本上是“退出”的同义词。)但我的猜测是,你说的是从其他应用程序收到的事件? (例如,将文件拖到应用程序中?)好吧,看看窗口消息。几乎所有东西都有一个。它们都由 WinProc 处理。因此,当您说“接收命令”时,如果可能的话,该操作会有某种 WM_*。
        • 感谢 InfiniteStack 更新代码 - 这非常有帮助!
        【解决方案5】:

        您可以使用 CreateWindow/CreateWindowEx 创建任意数量的窗口,它们之间的关系是您想要的(所有者/子)。

        您可以通过以下方式使窗口“拥有”:

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

        要将窗口转换为子窗口,请使用SetParent

        请注意,SetWindowLongPtrGWLP_HWNDPARENT 的调用不表现为 SetParent(我认为 MSDN 对此是错误的)。 GWLP_HWNDPARENT 不会将窗口转换为“子”,而是“拥有”。

        【讨论】:

        • 您通过将所有者窗口句柄传递给CreateWindow[Ex] 调用(hWndParent 参数)来创建所有者/拥有关系。如果WS_CHILD 窗口样式不存在,hWndParent 将是所有者。如果是,hWndParent 将指定父窗口,并建立父/子关系。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-12-31
        • 1970-01-01
        • 1970-01-01
        • 2016-11-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多