0

I'm trying to create independent window wrapper classes for my project. It's mostly working but cannot figure out how to get WM_QUIT in my main message pump. In the interest of learning Windows, I don't want to use other libraries for this.

This is a quick example of whats happening.

#include <iostream>
#include <Windows.h>

void TestPump()
{
    MSG msg = { 0 };

    PostQuitMessage(0);
    std::cout << "Posted WM_QUIT" << std::endl;

    while (true)
    {
        BOOL result = PeekMessage(&msg, (HWND) -1, 0, 0, PM_REMOVE);

        std::cout << "PeekMessage returned " << result << std::endl;

        if (result == 0)
            break;

        if (WM_QUIT == msg.message)
            std::cout << "got WM_QUIT" << std::endl;
    }
}

void MakeWindow()
{
    auto hwnd = CreateWindowEx(0, "Button", "dummy", 0, 0, 0, 32, 32, NULL, NULL, NULL, NULL);
    std::cout << std::endl << "Created Window" << std::endl << std::endl;
}

int main()
{
    TestPump();
    MakeWindow();
    TestPump();

    std::cin.get();

    return EXIT_SUCCESS;
}

The PeekMessage documentation is here: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644943(v=vs.85).aspx

I haven't been able to find any examples of using a -1 HWND filter, but MSDN says that it'll receive thread messages where the HWND is NULL (I've checked that this is true for WM_QUIT), which I believe PostQuitMessage does with WM_QUIT.

The problem only occurs if I create a Window.

Is there anything I'm doing wrong, or are there better methods?

Brady H
  • 25
  • 1
  • 3
  • 1
    I believe you meant "features" instead of "problems" in your closing sentence. – rubenvb Aug 24 '17 at 12:11
  • Just fyi, I started using WTL instead of my own wrapper, mostly because I was spending more time playing whackamole with Win32 than I was actually writing my application. MFC is a more heavyweight option. – Robinson Aug 24 '17 at 12:17
  • `PeekMessage(&msg, (HWND)-1, 0, 0, PM_REMOVE)` got `WM_QUIT` if `PostQuitMessage(0);` was called just before – RbMm Aug 24 '17 at 12:20
  • Shouldn't you call it like `PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)` to obtain `WM_QUIT`? – user7860670 Aug 24 '17 at 12:36
  • @RustyX - you confuse `GetMessage`, which really return `FALSE` on `WM_QUIT` and `PeekMessage` which got `WM_QUIT` – RbMm Aug 24 '17 at 12:42
  • 1
    `WM_QUIT` isn't a real message, it's a flag that's set on the thread that `PeekMessage` looks for. Presumably the window filter prevents it from doing this. See https://blogs.msdn.microsoft.com/oldnewthing/20051104-33/?p=33453 – Jonathan Potter Aug 24 '17 at 13:30
  • @JonathanPotter If WM_QUIT only returns if the queue is empty, that could be it. Even though I'm not asking for the other window's message in my loop, it's still non-empty. That begs the question however, is there a way to query the WM_QUIT flag? Otherwise, PostThreadMessage is probably a decent alternative. – Brady H Aug 24 '17 at 13:55
  • Fundamental problem is trying to run multiple message loops in tandem. That's a clear mistake. Correct that and all your problems vanish. – David Heffernan Aug 24 '17 at 14:25

2 Answers2

0

This seems to be an interesting case so I've created a mcve, though I'm struggling to explain this behavior:

#include <Windows.h>
#include <CommCtrl.h>

#include <iostream>
#include <cassert>

void
Create_ThreadMessagePump(void)
{
    ::MSG msg;
    ::PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
    ::std::wcout << L"initialized thread message pump" << ::std::endl;
}

void
Create_Window(void)
{
    auto const hwnd{::CreateWindowExW(0, WC_STATICW, L"dummy window", 0, 0, 0, 32, 32, NULL, NULL, NULL, NULL)};
    (void) hwnd; // not used
    assert(NULL != hwnd);
    ::std::wcout << L"created window" << ::std::endl;
}

void
Test_QuitMessageExtraction(void)
{
    ::PostQuitMessage(0);
    ::std::wcout << L"posted WM_QUIT" << ::std::endl;
    for(;;)
    {   
        ::MSG msg;
        auto const result{::PeekMessageW(&msg, (HWND) -1, 0, 0, PM_REMOVE)};
        ::std::wcout << L"PeekMessageW returned " << result << ::std::endl;
        if(0 == result)
        {
            ::std::wcout << L"no more messages to peek" << ::std::endl;
            break;
        }
        if(WM_QUIT == msg.message)
        {
            ::std::wcout << L"got WM_QUIT" << ::std::endl;
        }
    }
}

int
main()
{
    //Create_ThreadMessagePump(); // does not change anything
    Test_QuitMessageExtraction();
    Create_Window();
    Test_QuitMessageExtraction();
    system("pause");
    return(0);
}

output:

posted WM_QUIT
PeekMessageW returned 1
got WM_QUIT
PeekMessageW returned 0
no more messages to peek
created window
posted WM_QUIT
PeekMessageW returned 0
no more messages to peek
user7860670
  • 35,849
  • 4
  • 58
  • 84
  • I was just about to do this myself, which I should have done in the first place. Maybe I'll need to contact Mr. Chen. – Brady H Aug 24 '17 at 13:15
  • @BradyH If `PeekMessageW` returns 0 then `msg` contains garbage. `3435973836` is `0xCCCCCCCC` fill value for uninitialized variables in debug mode. – user7860670 Aug 24 '17 at 13:17
  • Yep I realized that as I posted it >.<. – Brady H Aug 24 '17 at 13:19
  • if replace `(HWND)-1` to 0 in call `PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)` all worked as must. so problem exactly in `(HWND)-1` – RbMm Aug 24 '17 at 13:27
  • also if call `PostThreadMessage(GetCurrentThreadId(), WM_QUIT, 0, 0);` instead `PostQuitMessage(0);` work with `PeekMessageW(&msg, (HWND)-1, 0, 0, PM_REMOVE)` – RbMm Aug 24 '17 at 13:30
  • @RbMm, For my experiment, that is undesirable, as I will get the messages from all the window classes, which I've given their own pump code. I only want non-window messages in this pump, which -1 is supposed to accomplish. There just appears to be some oddity stopping that from happening while any window exists. – Brady H Aug 24 '17 at 13:32
  • Good observation on the PostThreadMessage function. I wonder what the difference is. Perhaps something to do with an inconsistency with WM_QUIT being set as a flag or as a queue message when using PQM? – Brady H Aug 24 '17 at 13:34
  • @BradyH - really more than enough have single common message loop with `PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)`. I not view reason for what use multiple loops – RbMm Aug 24 '17 at 13:36
  • The only reason to ever run multiple message loops is when dealing with modality. Raymond Chen has a [whole series of articles](https://blogs.msdn.microsoft.com/oldnewthing/tag/modality) on how to implement modality correctly, and how modal message loops should be written. – Remy Lebeau Aug 24 '17 at 17:00
0

The problem appears to be with the way WM_QUIT and PostQuitMessage() operates. You can find information here:

Why is there a special PostQuitMessage function?.

In short, the WM_QUIT message will not be received while the queue is non-empty. Even if you filter out other messages by using a -1 HWND, the queue is still non-empty, and thus WM_QUIT is not returned. Creating of a window results in multiple HWNDs being created.

Brady H
  • 25
  • 1
  • 3