在单击菜单命令时使用createwindow()创建窗口

1szpjjfi  于 2023-04-29  发布在  其他
关注(0)|答案(2)|浏览(127)

我想在单击将成为主窗口子窗口的菜单项时使用CreateWindow()创建一个窗口。我知道我可以使用DialogBox()或CreateDialog(),但我想使用CreateWindow()。我在用这个密码
资源.rc文件

#include "resource.h"
IDM_MENU MENU
{
    POPUP "&Help"
    {
        MENUITEM "&About", IDM_HELP
    }
}

关于窗口过程

LRESULT CALLBACK AboutProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch(msg)
    {
    case WM_DESTROY:
        PostQuitMessage(0);
        break;

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

主窗口过程

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch(msg)
    {
    case WM_COMMAND:
        switch(LOWORD(wParam))
        {
        case IDM_HELP:
            {
                WNDCLASSEX wc;
                HWND hDlg;
                MSG msg;
                SecureZeroMemory(&wc, sizeof(WNDCLASSEX));
                wc.cbSize = sizeof(WNDCLASSEX);
                wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
                wc.hCursor = LoadCursor(0, IDC_ARROW);
                wc.hIcon = (HICON)GetClassLong(hwnd, GCL_HICON);
                wc.hIconSm = (HICON)GetClassLong(hwnd, GCL_HICONSM);
                wc.hInstance = GetModuleHandle(0);
                wc.lpfnWndProc = AboutProc;
                wc.lpszClassName = TEXT("AboutClass");          

                if(!RegisterClassEx(&wc))
                    break;

                hDlg = CreateWindowEx(0, wc.lpszClassName, TEXT("About"), WS_OVERLAPPEDWINDOW, 0, 0, 300, 200, hwnd, 0, wc.hInstance, 0);

                ShowWindow(hDlg, SW_SHOWNORMAL);

                while(GetMessage(&msg, 0, 0, 0) > 0)
                {
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
                UnregisterClass(wc.lpszClassName, wc.hInstance);
            }
            break;
        }
        break;

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

这是个好主意吗?您可以在同一个示例中注册多个类吗?另外,将主窗口图标分配给这个子窗口是一个好主意,还是每次都加载它们?当我在IDM_HELP中调用UnregisterClass()时,这些图标会被删除吗?我试过这个程序,一切正常,图标仍然显示在主窗口后,我关闭这个子窗口。但是我仍然怀疑是否可以将主窗口图标分配给这个窗口,因为我在子窗口关闭后调用了UnregisterClass()

dbf7pr2w

dbf7pr2w1#

使用CreateWindow/Ex()而不是CreateDialog()/DialogBox()没有任何问题。运行你自己的模态消息循环没有什么错,只要你正确地实现它。例如,请注意以下警告:
Modality, part 3: The WM_QUIT message
关于模态的另一个重要的事情是WM_QUIT消息总是打破模态循环。在你自己的模态循环中记住这一点!如果调用PeekMessage函数或GetMessage函数并获得WM_QUIT消息,则不仅必须退出模态循环,而且还必须重新生成WM_QUIT消息(通过PostQuitMessage消息),以便下一个外层将看到WM_QUIT消息并进行清理。如果您未能传播消息,下一个外层将不知道它需要退出,并且程序将似乎“卡在”其关闭代码中,迫使用户以艰难的方式终止进程。
你展示的例子没有做到这一点,所以你需要添加它:

ShowWindow(hDlg, SW_SHOWNORMAL);

do
{
    BOOL bRet = GetMessage(&msg, 0, 0, 0);
    if (bRet > 0)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    else
    {
        if (bRet == 0) 
            PostQuitMessage(msg.wParam); // <-- add this!
        break;
    }
}
while (1);

UnregisterClass(wc.lpszClassName, wc.hInstance);

然而,你的模态窗口不应该仅仅为了打破它的模态循环而使用WM_QUIT,因为这样做会退出你的整个应用程序!当窗口关闭时,使用不同的信号使模态循环中断。例如:

ShowWindow(hDlg, SW_SHOWNORMAL);

while (IsWindow(hDlg) && (GetMessage(&msg, 0, 0, 0) > 0))
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}
LRESULT CALLBACK AboutProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch(msg)
    {
    case WM_CLOSE:
        DestroyWindow(hwnd);
        break;

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

    return 0;
}

此外,模态窗口应该禁用其所有者窗口,然后在关闭时重新启用它。你的例子也没有做到这一点,所以也需要添加:

ShowWindow(hDlg, SW_SHOWNORMAL);
EnableWindow(hwnd, FALSE); // <-- add this
...
LRESULT CALLBACK AboutProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch(msg)
    {
    case WM_CLOSE:
        EnableWindow(GetWindow(hwnd, GW_OWNER), TRUE); // <-- add this
        DestroyWindow(hwnd);
        break;

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

    return 0;
}

Old New Thing博客有一系列关于如何使用模态窗口的文章。
Modality, part 1: UI-modality vs code-modality
Modality, part 2: Code-modality vs UI-modality
Modality, part 3: The WM_QUIT message
Modality, part 4: The importance of setting the correct owner for modal UI
Modality, part 5: Setting the correct owner for modal UI
Modality, part 6: Interacting with a program that has gone modal
Modality, part 7: A timed MessageBox, the cheap version
Modality, part 8: A timed MessageBox, the better version
Modality, part 9: Setting the correct owner for modal UI, practical exam
The correct order for disabling and enabling windows
Make sure you disable the correct window for modal UI

更新:根据您的评论“不,我不想要模态窗口”,您可以忽略上面的所有内容。所有这些都只适用于模式窗口。既然您不想要模态窗口,那么只需完全删除辅助循环,让主消息循环处理所有事情。另外,您不需要调用UnregisterClass()。它将在进程结束时自动注销。调用RegisterClass()一次,可以在程序启动时,或者至少在第一次显示“About”窗口时。您可以使用GetClassInfo/Ex()来了解类是否已经注册,或者自己跟踪它。试想一下,如果用户希望在进程的生存期内多次显示“关于”窗口,会发生什么情况。因此,让它每次都重用现有的类注册。

试试这个:

LRESULT CALLBACK AboutProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch(msg)
    {
        case WM_CLOSE:
            DestroyWindow(hwnd);
            break;

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

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch(msg)
    {
        case WM_COMMAND:
            switch(LOWORD(wParam))
            {
                case IDM_HELP:
                {
                    WNDCLASSEX wc = {0};
                    wc.cbSize = sizeof(WNDCLASSEX);
                    wc.hInstance = GetModuleHandle(0);
                    wc.lpszClassName = TEXT("AboutClass");          

                    if (!GetClassInfoEx(wc.hInstance, wc.lpszClassName, &wc))
                    {
                        wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
                        wc.hCursor = LoadCursor(0, IDC_ARROW);
                        wc.hIcon = (HICON)GetClassLong(hwnd, GCL_HICON);
                        wc.hIconSm = (HICON)GetClassLong(hwnd, GCL_HICONSM);
                        wc.lpfnWndProc = AboutProc;

                        if (!RegisterClassEx(&wc))
                            break;
                    }

                    HWND hDlg = CreateWindowEx(0, wc.lpszClassName, TEXT("About"), WS_OVERLAPPEDWINDOW, 0, 0, 300, 200, hwnd, 0, wc.hInstance, 0);

                    if (hDlg)
                        ShowWindow(hDlg, SW_SHOWNORMAL);
                }
                break;
            }
            break;

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

byqmnocz2#

可以使用CreateWindow()。你可以在你的事件处理器中做任何事情。使用DialogBox()只是免费提供了一个模态循环(因此在对话框关闭之前,无法与主窗口交互)。
是的,您可以注册多个窗口类。你可以自由地提前注册你所有的窗口课程;你不需要每次有人点击你的菜单项时调用RegisterClass()UnregisterClass()
我不确定UnregisterClass()是否释放了分配给窗口类的各种GDI资源;任何知道答案的人都可以自由发表评论。

相关问题