请问C语言如何实现多个窗口的嵌套?

我在学习C语言win窗口编程的时候,写了如下基础的程序:
#include "windows.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) {
    WNDCLASS wc_1;
    WNDCLASS wc_2;

    static TCHAR* NAME_1 = TEXT("one");
    static TCHAR* NAME_2 = TEXT("two");

    HWND hwnd_1 = NULL;
    HWND hwnd_2 = NULL;

    MSG msg_1;
    MSG mag_2;

    wc_1.style = CS_HREDRAW | CS_VREDRAW; //窗口样式
    wc_1.lpfnWndProc = WndProc; //过程函数
    wc_1.cbClsExtra = 0; //扩展字段
    wc_1.cbWndExtra = 0; //扩展字段
    wc_1.hInstance = hInstance; //当前实例句柄
    wc_1.hIcon = LoadIcon(hInstance, IDI_APPLICATION); //设置程序图标
    wc_1.hCursor = LoadCursor(NULL, IDC_ARROW); //设置鼠标
    wc_1.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH);
    wc_1.lpszMenuName = NULL;
    wc_1.lpszClassName = NAME_1;

    RegisterClass(&wc_1);
    RegisterClass(&wc_2);

    hwnd_1 = CreateWindow(NAME_1, //要注册的窗口类名
                        TEXT("The first window"),//窗口标题
                        WS_OVERLAPPEDWINDOW, //窗口样式
                        CW_USEDEFAULT, //窗口距离屏幕左上角都横坐标
                        CW_USEDEFAULT, //窗口距离屏幕左上角都纵坐标
                        400, //窗口宽度
                        300, //窗口高度
                        NULL, //父窗口句柄
                        NULL, //菜单句柄
                        hInstance, //当前实例句柄
                        NULL); //指向一个值的指针,该值传递给窗口 WM_CREATE消息。一般为NULL
                        
    ShowWindow(hwnd_1, iCmdShow);

    UpdateWindow(hwnd_1);

    while (GetMessage(&msg_1, NULL, 0, 0)) {
        TranslateMessage(&msg_1);
        DispatchMessage(&msg_1);
    }

    return msg_1.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
    switch (message) {
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

这样只能得到一个黑黢黢的窗口,没有什么用途。为了学习我下载了一款定时打铃的简易软件,用vs的spy++工具发现这个程序可以在一个窗口内嵌套另一些别的窗口:

img

img

img

img

在大窗口内存在很多小窗口,它们有的窗口类是一样的,但句柄都是不一样的,这样有很多窗口就可以让这个程序更加美观,功能更齐全,使用起来更方便。
请问使用C语言如何才可以实现这种窗口的嵌套,并实现多功能美观的应用程序?感谢!

参考GPT和自己的思路:实现窗口嵌套需要在窗口过程函数中处理WM_CREATE消息,以创建子窗口,并在父窗口的WM_SIZE消息中调整子窗口的大小和位置。以下是一个简单的示例代码,可以创建两个嵌套的窗口。

#include <windows.h>

LRESULT CALLBACK ParentWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK ChildWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) {
    WNDCLASS wc;
    HWND hwndParent, hwndChild;
    MSG msg;

    // 注册父窗口类
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = ParentWndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = TEXT("ParentWndClass");
    RegisterClass(&wc);

    // 注册子窗口类
    wc.lpfnWndProc = ChildWndProc;
    wc.lpszClassName = TEXT("ChildWndClass");
    RegisterClass(&wc);

    // 创建父窗口
    hwndParent = CreateWindow(TEXT("ParentWndClass"), TEXT("Parent Window"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 300, NULL, NULL, hInstance, NULL);

    // 创建子窗口
    hwndChild = CreateWindow(TEXT("ChildWndClass"), TEXT("Child Window"), WS_CHILD | WS_VISIBLE, 50, 50, 200, 150, hwndParent, NULL, hInstance, NULL);

    ShowWindow(hwndParent, iCmdShow);
    UpdateWindow(hwndParent);

    // 消息循环
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int)msg.wParam;
}

LRESULT CALLBACK ParentWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch (msg) {
        case WM_CREATE:
            // 创建子窗口
            CreateWindow(TEXT("ChildWndClass"), TEXT("Child Window"), WS_CHILD | WS_VISIBLE, 50, 50, 200, 150, hwnd, NULL, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
            break;

        case WM_SIZE:
            // 调整子窗口的大小和位置
            MoveWindow(GetDlgItem(hwnd, 1), 50, 50, LOWORD(lParam) - 100, HIWORD(lParam) - 100, TRUE);
            break;

        case WM_DESTROY:
            PostQuitMessage(0);
            break;

        default:
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }

    return 0;
}

LRESULT CALLBACK ChildWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch (msg) {
        case WM_CREATE:
            break;

        case WM_PAINT:
            {
                PAINTSTRUCT ps;
                HDC hdc = BeginPaint(hwnd, &ps);
                TextOut(hdc, 10, 10, TEXT("Hello from child window!"), 25);
EndPaint(hwnd, &ps);
}
break;
case WM_DESTROY:
        PostQuitMessage(0);
        break;

    default:
        return DefWindowProc(hwnd, msg, wParam, lParam);
}

return 0;
}
LRESULT CALLBACK ParentWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
static HWND child_hwnd = NULL;
switch (msg) {
    case WM_CREATE:
        {
            WNDCLASS wc = { 0 };

            wc.style = CS_HREDRAW | CS_VREDRAW;
            wc.lpfnWndProc = ChildWndProc;
            wc.hInstance = (HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
            wc.lpszClassName = TEXT("Child");

            RegisterClass(&wc);

            child_hwnd = CreateWindow(TEXT("Child"), TEXT("Child Window"),
                WS_CHILD | WS_VISIBLE, 50, 50, 200, 200,
                hwnd, NULL, wc.hInstance, NULL);
        }
        break;

    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);
            TextOut(hdc, 10, 10, TEXT("Hello from parent window!"), 27);
            EndPaint(hwnd, &ps);
        }
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        break;

    default:
        return DefWindowProc(hwnd, msg, wParam, lParam);
}

return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) {
static TCHAR szAppName[] = TEXT("Parent");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = ParentWndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;

if (!RegisterClass(&wndclass)) {
    MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR);
    return 0;
}

hwnd = CreateWindow(szAppName, TEXT("The Hello Program"), WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
    NULL, NULL, hInstance, NULL);

ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);

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

return msg.wParam;
}

依次调用 setparent 就可以相互嵌套了。

该回答引用GPTᴼᴾᴱᴺᴬᴵ
在Win32 API中,可以使用CreateWindowEx函数创建一个子窗口,然后将其作为父窗口的一个子窗口。您可以使用窗口样式WS_CHILD将其设置为子窗口,然后使用SetParent函数将其添加为父窗口的子窗口。

下面是一个简单的示例,它创建了两个窗口,其中一个窗口包含两个子窗口:

#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    WNDCLASS wc = {0};
    HWND hwnd, hwndButton, hwndEdit;
    MSG msg;

    // 注册窗口类
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    wc.lpszClassName = L"MyClass";
    RegisterClass(&wc);

    // 创建父窗口
    hwnd = CreateWindow(wc.lpszClassName, L"MyWindow", WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, NULL, NULL, hInstance, NULL);
    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    // 创建一个按钮控件
    hwndButton = CreateWindow(L"BUTTON", L"Click me!", WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
        50, 50, 100, 30, hwnd, (HMENU)1, hInstance, NULL);

    // 创建一个文本框控件
    hwndEdit = CreateWindow(L"EDIT", L"", WS_VISIBLE | WS_CHILD | WS_BORDER | ES_MULTILINE,
        50, 100, 200, 150, hwnd, (HMENU)2, hInstance, NULL);

    // 消息循环
    while(GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch(msg) {
        case WM_COMMAND:
            switch(LOWORD(wParam)) {
                case 1:
                    MessageBox(hwnd, L"Button clicked!", L"Hello", MB_OK);
                    break;
            }
            break;

        case WM_DESTROY:
            PostQuitMessage(0);
            break;

        default:
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }

    return 0;
}


在这个示例中,我们创建了一个父窗口 hwnd,然后在父窗口中创建了一个按钮控件 hwndButton 和一个文本框控件 hwndEdit,它们的父窗口都是 hwnd。注意到,在创建子窗口时,我们使用了 hwnd 作为最后一个参数,这就是指定
子窗口的父窗口句柄,也就是将子窗口作为父窗口的一个子控件来创建。
-
具体来说,hwndButton 是一个按钮控件,我们在创建时指定了它的风格为 WS_CHILD,这表示它是一个子窗口,并且它的父窗口是 hwnd。hwndEdit 是一个文本框控件,我们同样指定了它的风格为 WS_CHILD,这样它就成为了 hwnd 的一个子控件。
-
在消息处理函数中,我们处理了 WM_CREATE 和 WM_COMMAND 两个消息。在 WM_CREATE 消息中,我们创建了 hwndButton 和 hwndEdit,设置了它们的初始位置和大小,并将 hwnd 保存到它们的用户数据中。在 WM_COMMAND 消息中,我们判断是哪个控件发送了消息,如果是 hwndButton,则弹出一个对话框,如果是 hwndEdit,则获取编辑框中的文本并显示在对话框中。
-
这样,我们就实现了在一个父窗口中创建多个子控件的功能。在实际的应用中,可以根据需要创建更多的子控件,并根据消息处理函数中的逻辑实现各种功能。

参考GPT和自己的思路,要在一个窗口内嵌套其他窗口,你需要创建一个父窗口,然后在父窗口内创建多个子窗口。每个子窗口都有自己的窗口句柄和窗口过程函数。可以使用CreateWindow函数来创建窗口,并指定父窗口句柄作为其中一个参数,将它们嵌套在父窗口中。

下面是一个简单的示例程序,展示了如何创建一个具有多个子窗口的父窗口:

#include <windows.h>

// 父窗口的窗口过程函数
LRESULT CALLBACK ParentWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
    switch (message) {
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hwnd, message, wParam, lParam);
    }
    return 0;
}

// 子窗口1的窗口过程函数
LRESULT CALLBACK ChildWndProc1(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
    switch (message) {
        // 处理子窗口消息
        default:
            return DefWindowProc(hwnd, message, wParam, lParam);
    }
    return 0;
}

// 子窗口2的窗口过程函数
LRESULT CALLBACK ChildWndProc2(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
    switch (message) {
        // 处理子窗口消息
        default:
            return DefWindowProc(hwnd, message, wParam, lParam);
    }
    return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) {
    WNDCLASS wc;
    HWND hwnd;
    HWND child1, child2;
    MSG msg;

    // 注册父窗口类
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = ParentWndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = TEXT("ParentClass");
    RegisterClass(&wc);

    // 创建父窗口
    hwnd = CreateWindow(TEXT("ParentClass"), TEXT("Parent Window"), WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, NULL, NULL, hInstance, NULL);
    ShowWindow(hwnd, iCmdShow);
    UpdateWindow(hwnd);

    // 注册子窗口1类
    wc.style = 0;
    wc.lpfnWndProc = ChildWndProc1;
    wc.hInstance = hInstance;
    wc.hIcon = NULL;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = TEXT("ChildClass1");
    RegisterClass(&wc);

// 创建子窗口1
child1 = CreateWindow(TEXT("ChildClass1"), TEXT("Child Window 1"), WS_CHILD | WS_VISIBLE,
                      10, 10, 200, 200, hwnd, (HMENU)1, hInstance, NULL);

// 创建子窗口2
child2 = CreateWindow(TEXT("ChildClass2"), TEXT("Child Window 2"), WS_CHILD | WS_VISIBLE,
                      220, 10, 200, 200, hwnd, (HMENU)2, hInstance, NULL);

// 创建子窗口3
child3 = CreateWindow(TEXT("ChildClass1"), TEXT("Child Window 3"), WS_CHILD | WS_VISIBLE,
                      10, 220, 200, 200, hwnd, (HMENU)3, hInstance, NULL);

// 创建子窗口4
child4 = CreateWindow(TEXT("ChildClass2"), TEXT("Child Window 4"), WS_CHILD | WS_VISIBLE,
                      220, 220, 200, 200, hwnd, (HMENU)4, hInstance, NULL);

// 窗口显示
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);

  1. 定义一个窗口结构体,包含窗口的长、宽、坐标等信息
typedef struct {
    int length;   // 窗口长度
    int width;    // 窗口宽度
    int x;        // 窗口横坐标
    int y;        // 窗口纵坐标
} Window;
  1. 定义一个包含多个窗口的主窗口结构体。
typedef struct {
    int num_of_windows;  // 窗口个数
    Window window[];     // 包含多个子窗口
} Main_window;
  1. 在主函数中,定义一个主窗口,并初始化其中的子窗口。
int main() {
    Main_window mainWindow;
    mainWindow.num_of_windows = 2;
    mainWindow.window[0].length = 20;
    mainWindow.window[0].width = 10;
    mainWindow.window[0].x = 0;
    mainWindow.window[0].y = 0;
    mainWindow.window[1].length = 30;
    mainWindow.window[1].width = 15;
    mainWindow.window[1].x = 10;
    mainWindow.window[1].y = 10;
    return 0;
}
  1. 实现窗口的显示和操作,可以使用图形库,如graphics.h、SDL等。

注意:这只是实现多窗口的一种方式,具体还可以根据需要进行修改。同时,不同的平台上,窗口的实现方式可能也不同,需要根据实际情况进行处理。