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

时间:2015-03-31 01:35:37

标签: c winapi

我想在单击将成为主窗口子项的菜单项时使用CreateWindow()创建一个窗口。我知道我可以使用DialogBox()或CreateDialog(),但我想使用CreateWindow()。我使用此代码

resource.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()

2 个答案:

答案 0 :(得分:2)

是的,您可以使用CreateWindow()。您可以在事件处理程序中执行任何。使用DialogBox()只会为您提供一个免费的模态循环(因此在关闭对话框之前,您的主窗口无法进行交互)。

是的,您可以注册多个窗口类。您可以提前注册所有窗口课程;每次有人点击您的菜单项时,您都不需要致电RegisterClass()UnregisterClass()

我不确定UnregisterClass()是否释放了分配给您的窗口类的各种GDI资源;任何知道答案的人都可以自由发表评论。

答案 1 :(得分:2)

使用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

更新:根据您的评论&#34;不,我不想要模态窗口&#34;,您可以忽略上述所有内容。所有这些仅适用于模态窗口。由于您不需要模态窗口,只需完全删除辅助循环并让主消息循环处理所有内容。此外,您无需致电UnregisterClass()。它将在进程结束时自动取消注册。在程序启动时或至少在第一次显示“关于”窗口时,调用RegisterClass()一次。您可以使用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;
}