【问题标题】:the WM_CLOSE event is never sent/received?WM_CLOSE 事件永远不会发送/接收?
【发布时间】:2020-10-14 15:21:25
【问题描述】:

我正在学习 DX12,并在此过程中学习“好老的 Win32”。 我无法退出主循环,这似乎与我没有收到 WM_CLOSE 消息有关。

在 C++、Windows 10、控制台应用程序中。

#include <iostream>
#include <d3d12.h>
#include <dxgi1_4.h>
#include <tchar.h>

LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    return ::DefWindowProc(hWnd, msg, wParam, lParam);
}


int main()
{
    std::cout << "Hello World!\n";

    WNDCLASSEX wc = {
        sizeof(WNDCLASSEX),
        CS_CLASSDC,
        WndProc,
        0L, 0L,
        GetModuleHandle(NULL),
        NULL, NULL, NULL, NULL,
        _T("ker engine"),
        NULL
    };

    std::cout << "Registering Class\n";
    ::RegisterClassEx(&wc);

    std::cout << "Creating Window\n";
    HWND hwnd = ::CreateWindow(
        wc.lpszClassName,
        _T("Ker Engine DX12"),
        WS_OVERLAPPEDWINDOW,
        100, 100, 1280, 800, NULL, NULL,
        wc.hInstance, NULL
    );

    std::cout << "Show Window\n";
    ::ShowWindow(hwnd, SW_SHOWDEFAULT);

    std::cout << "Update Window\n";
    ::UpdateWindow(hwnd);

    std::cout << "Entering main loop\n";
    MSG msg;
    ZeroMemory(&msg, sizeof(msg));

    while (msg.message != (WM_QUIT))
    {
        if (::PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
        {
            ::TranslateMessage(&msg);
            ::DispatchMessage(&msg);
            std::cout << msg.message << std::endl;
            switch (msg.message)
            {
            case WM_CLOSE:
                std::cout << "close received\n";
                ::PostQuitMessage(0);
                break;
            }
            continue;
        }

    }
    std::cout << "leaving main loop\n";

    std::cout << "Destroy Window\n";
    ::DestroyWindow(hwnd);
    std::cout << "Unregister Class\n";
    ::UnregisterClass(wc.lpszClassName, wc.hInstance);
    std::cout << "Bye\n";

    return 0;
}

当我按下 X(关闭)红色窗口按钮时,窗口已关闭,但是:

  • 未打印“已关闭收到”
  • “离开主循环”没有打印出来。

输出是:

Entering main loop
[a lot of message code, in decimal]
160  (a lot of it) (WM_NCMOUSEMOVE)
161  (WM_NCLBUTTONDOWN)
275  (WM_TIMER)
no more output printed, i have to close the console manually.

没有 WM_CLOSE、WM_DESTROY 或 WM_QUIT。在 BUTTONDOW 和任何 TIMER 应该是之间,应该有一个与窗户关闭的事实相关的事件,不是吗?

我是这方面的初学者。我试图搜索 google 和 stackoverflow,但我不明白上下文是否适用于我,或者它太具体/不相关。它可能是重复的,但我找不到它。

我可能会丢失/跳过消息吗?我能想到的就这些了。

【问题讨论】:

  • 你的程序在做什么?作为用户,您在使用该程序做什么?你在按下窗户关闭按钮吗?请尝试将您的代码简化为minimal reproducible example 并展示给我们。
  • 正确的 minimal reproducible example 将是 gist 链接中的代码,但没有注释“链接”到文档或类似的 cmets。此外,强烈建议不要链接到代码。链接可能会消失,或者内容可能会发生变化,从而可能使问题无法回答。问题需要独立。
  • 如果你有一个控制台应用程序,你不需要一个消息循环(除非你正在做一些 COM 的东西或其他的东西,也就是:消息与窗口应用程序不同)。如果你有一个窗口应用程序,不要试图重新发明轮子,从这个教程开始:docs.microsoft.com/en-us/windows/win32/learnwin32/…
  • 等等,我明白了。我已经有了这个 WndProc 函数。这是我应该将我的 Windows 事件消息处理放在哪里,而不是在循环中,对吧?
  • 是的!就是这样!我在 WndProc 中移动了开关,现在它可以工作了。虽然帮助有点复杂,但它确实解决了我的问题。谢谢

标签: c++ windows winapi win32gui


【解决方案1】:

感谢 Simon Mourier 的评论和教程链接,问题得到了解决。

消息处理必须在 WndProc 中完成,而不是在“主循环”中。

我正在重新发布经过修改、清理、工作的代码:

#include <iostream>
#include <d3d12.h>
#include <dxgi1_4.h>
#include <tchar.h>

LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_CLOSE:
        std::cout << "close received\n";
        ::PostQuitMessage(0);
        return 0;
    }
    return ::DefWindowProc(hWnd, msg, wParam, lParam);
}


int main()
{
    WNDCLASSEX wc = {
        sizeof(WNDCLASSEX),
        CS_CLASSDC,
        WndProc,
        0L, 0L,
        GetModuleHandle(NULL),
        NULL, NULL, NULL, NULL,
        _T("ker engine"),
        NULL
    };

    ::RegisterClassEx(&wc);

    HWND hwnd = ::CreateWindow(
        wc.lpszClassName,
        _T("Ker Engine DX12"),
        WS_OVERLAPPEDWINDOW,
        100, 100, 1280, 800, NULL, NULL,
        wc.hInstance, NULL
    );

    ::ShowWindow(hwnd, SW_SHOWDEFAULT);
    ::UpdateWindow(hwnd);

    MSG msg;
    ZeroMemory(&msg, sizeof(msg));
    while (msg.message != (WM_QUIT))
    {
        if (::PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
        {
            ::TranslateMessage(&msg);
            ::DispatchMessage(&msg);
        }    
    }
    ::DestroyWindow(hwnd);
    ::UnregisterClass(wc.lpszClassName, wc.hInstance);

    return 0;
}

【讨论】:

  • 离题:PeekMessage 将忙循环。有什么理由不使用GetMessage
  • 虽然代码看起来可以工作,但它仍然存在缺陷。 PostQuitMessage 应该用于最后一个窗口,因为它会停止消息循环(或者代码应该像“我只有一​​个窗口”这样的注释)并且它应该对 WM_DESTROY 做出反应,而不是 WM_CLOSE:stackoverflow.com/questions/16749182/…
  • @Paul Sanders:不,没有理由。我会检查。谢谢。
  • @simon :我确实只有一个窗口。但我会考虑并进行相应修改(一旦我了解其含义和差异)
  • 这可能会有所帮助:stackoverflow.com/questions/3155782/…
猜你喜欢
  • 1970-01-01
  • 2016-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-28
  • 2018-02-20
相关资源
最近更新 更多