在Windows API中,我正在研究GetMessage函数的实际工作方式。我已经看过Windows消息循环的3种实现,并希望对其进行探讨。

1)

在撰写本文时,this MSDN article描述了我认为是实现消息循环的正确方法。

MSG msg;
BOOL bRet;
while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
    if (bRet == -1)
    {
        // handle the error and possibly exit
    }
    else
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

2)

GetMessage function page上,我看到了以下实现:
MSG msg;
BOOL bRet;
while( (bRet = GetMessage( &msg, hWnd, 0, 0 )) != 0)
{
    if (bRet == -1)
    {
        // handle the error and possibly exit
    }
    else
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

3)

最后,Visual Studio documentation将此实现作为其Win32 Application演示的一部分。
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

讨论

简而言之,实现方案3忽略了从GetMessage返回的错误,但其他方面与第一个实现方案相同。也就是说,它们都处理当前线程的所有消息。当GetMessage函数返回0时,循环终止。

由于我在#1之前找到了实现#2,因此我认为它已经完成。但是,我注意到通过 GetMessage 发布0消息时WM_QUIT不会返回PostQuitMessage
这导致了一些困惑,直到我找到实现#1并对其进行了测试。前两个实现之间的区别是GetMessage的第二个参数。在#2中,它指定hWnd,根据GetMessage文档,它是:



在#1中,是NULL,它与以下摘录有关:



当使用NULL进行测试时,GetMessage函数在处理0消息时会返回WM_QUIT,从而成功终止循环。

问题
  • 即使从特定窗口的回调函数调用了PostQuitMessage,但WM_QUIT实际上是否属于该窗口或当前线程?根据对这三个实现的测试,它似乎与当前线程相关联。
  • 如果与线程关联,何时使用有效的hWnd作为GetMessage的参数有用或合适?这样的消息循环将无法返回0作为对WM_QUIT的响应,那么消息循环是否应该终止?

  • 引用
  • GetMessage
  • PostQuitMessage
  • Message Loop #1 (Wikipedia Link)
  • Message Loop #2
  • Message Loop #3
  • WM_QUIT Message
  • Related Question

  • 代码
    #include <Windows.h>
    #include <tchar.h>
    #include <strsafe.h>
    
    LRESULT CALLBACK WndProc(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;
    }
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR cmdLine, int nCmdShow) {
        LPCTSTR wndClassName =_T("Class_SHTEST");
        LPCTSTR wndName = _T("SHTest");
    
        WNDCLASSEX wcex;
        wcex.cbSize = sizeof(WNDCLASSEX);
        wcex.style = CS_HREDRAW|CS_VREDRAW;
        wcex.lpfnWndProc = WndProc;
        wcex.cbClsExtra = 0;
        wcex.cbWndExtra = 0;
        wcex.hInstance = hInstance;
        wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
        wcex.hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
        wcex.hbrBackground = (HBRUSH) COLOR_WINDOW+1;
        wcex.lpszMenuName = NULL;
        wcex.lpszClassName = wndClassName;
        wcex.hIconSm = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
    
        if (!RegisterClassEx(&wcex))
        {
            MessageBox(NULL, _T("Call to RegisterClassEx failed!"), wndName, MB_OK|MB_ICONERROR);
        }
    
        HWND window = CreateWindow(wndClassName, wndName,
            WS_OVERLAPPEDWINDOW | WS_MAXIMIZE,
            0, 0, CW_USEDEFAULT, CW_USEDEFAULT,
            NULL, NULL, hInstance, NULL);
        if (!window) {
            MessageBox(NULL, _T("Call to CreateWindow failed!"), wndName, MB_OK|MB_ICONERROR);
        }
    
        ShowWindow(window, SW_SHOW);
        UpdateWindow(window);
    
        //Message loop (using implementation #1)
        MSG msg;
        BOOL bRet;
        while((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) {
            if (bRet == -1) {
                //Handle error and possibly exit.
            }
            else {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }
    
        //Return the exit code in the WM_QUIT message.
        return (int) msg.wParam;
    }
    

    最佳答案

    根据WM_QUITMSDN documention:



    由于WM_QUIT与窗口不相关,并且将HWND传递给GetMessage()仅检索与该窗口相关联的那些消息,因此后者在设计上将永远不会收到WM_QUIT

    至于何时要将HWND传递给GetMessage(),则不需要在应用程序的常规消息循环中传递。但是有时候,您希望在UI中发生某些事情时泵送消息,而只关心与特定窗口关联的消息。

    关于c++ - WM_QUIT只为线程而不是窗口发帖吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/32768924/

    10-10 21:31