edition
edition

Reputation: 678

Window in Win32 application won't close normally

Original Problem:

I have a window that cannot be closed immediately after being created despite my window procedure function being able to detect the WM_CLOSE message, and calling PostQuitMessage and letting Windows continue to handle the window messages with DefWindowProc.

But, after moving the window by dragging the title bar, it seems to be able to close normally.

The code for the my window procedure function is the following:

LRESULT CALLBACK OnEvent(HWND handle, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
        case WM_QUIT:
        {
            printf("WM_QUIT\n");
            break;
        }
        case WM_CLOSE:
        {
            printf("WM_CLOSE\n");
            PostQuitMessage(0);
            return 0;
        }
    }
    return DefWindowProc(handle, message, wParam, lParam);
}

So besides sending the WM_MOVE message to my window or using exit(0) in the WM_CLOSE case block, how can I ensure that my window can be closed immediately after being created?

New Question:

After passing NULL to the hWnd parameter of GetMessageW, instead of my window handle, the window now closes properly hence answering my previous question.

Therefore, why did the GetMessageW function previously not retrieve the WM_QUIT message with the provided handle of the only window, instead of using NULL as the hWnd parameter?

Upvotes: 1

Views: 3112

Answers (3)

aesuu
aesuu

Reputation: 1

Another issue that I had found was that when you had set your window classes (WNDCLASSEX) windows procedure (lpfnWndProc) I had set it to the default window procedure and had not used a custom built window procedure. So when you had closed it nothing would occur.

Upvotes: 0

synthetic
synthetic

Reputation: 23

I had this exact problem when I called GetMessage() in my loop with the handle of the main window as the second argument:

GetMessage(&msg, hMainWindow, 0, 0)

instead of NULL to specify that I want to handle messages for the whole thread / application instance:

GetMessage(&msg, NULL, 0, 0)

The WM_CLOSE messages would lead to WM_QUIT messages, but GetMessage() will ignore these when it's only looking for messages for a particular window. From the documentation:

If hWnd is NULL, GetMessage retrieves messages for any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL (see the MSG structure). Therefore if hWnd is NULL, both window messages and thread messages are processed.

What exactly causes moving of the window to have subsequent closing attempts succeed is however still beyond me.

Upvotes: 0

Remy Lebeau
Remy Lebeau

Reputation: 597961

A properly written message loop will never dispatch a WM_QUIT message to a window procedure, it will simply break the loop when GetMessage() returns 0 when it receives a WM_QUIT message:

GetMessage function

If the function retrieves a message other than WM_QUIT, the return value is nonzero.

If the function retrieves the WM_QUIT message, the return value is zero.

If there is an error, the return value is -1. For example, the function fails if hWnd is an invalid window handle or lpMsg is an invalid pointer. To get extended error information, call GetLastError.

In response to WM_CLOSE, you should be calling DestroyWindow() instead of PostQuitMessage() directly (the default behavior of DefWindowProc(WM_CLOSE) is to call DestroyWindow() for you). You need a WM_DESTROY handler that should then call PostQuitMessage():

LRESULT CALLBACK OnEvent(HWND handle, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
        case WM_CLOSE:
        {
            printf("WM_CLOSE\n");
            DestroyWindow(handle);
            return 0;
        }

        case WM_DESTROY:
        {
            printf("WM_DESTROY\n");
            PostQuitMessage(0);
            return 0;
        }
    }
    return DefWindowProc(handle, message, wParam, lParam);
}

This is documented on MSDN:

Closing the Window

Here is the flow chart from that page:

flow chart

As for the GetMessage() issue, if you read the documentation for GetMessage() and PostQuitMessage() more carefully, you will see that the WM_QUIT message posted by PostQuitMessage() is not a window message. Passing a non-NULL HWND to GetMessage() only retrieves messages from PostMessage() and SendMessage() that are intended for that specific HWND 1. GetMessage() will ignore any messages that are not intended for that HWND, which includes thread messages from PostThreadMessage() and PostQuitMessage(). Passing a NULL HWND to GetMessage() allows it to return any pending message, including WM_QUIT.

1 which is dangerous to do, BTW: The dangers of filtering window messages

Upvotes: 9

Related Questions