Problem in the Windows platform layer (app not closing properly)

Hello, I have encountered a somewhat confusing (atleast for me) problem.

The problem is that when I close my application using the cross in the upper right corner or by hitting alt+f4 the application closes down properly and the process exits.

However when I close the application from the taskbar either by hitting the cross on the window thumbnail or by right clicking and selecting close, then the window gets destroyed but the process never exits.

Here is my code that handles these parts.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
LRESULT CALLBACK
WindowProc(HWND Window, UINT Message, WPARAM WParam, LPARAM LParam)
{
    LRESULT Result;
    switch(Message)
    {
 
...

        case WM_CLOSE:
        {
            DestroyWindow(Window);
            // NOTE(pete): (from MSDN) If an application processes this
            // message, it should return zero.
            Result = 0;
        } break;

        case WM_DESTROY:
        {
            // TODO(pete): There is a super annoying bug where the app wont
            // quit properly when closing from the taskbar? The window gets
            // destroyed but no WM_QUIT message seems to be posted/recieved?
            PostQuitMessage(0);
            // NOTE(pete): (from MSDN) If an application processes this
            // message, it should return zero.
            Result = 0;
        } break;

...

        default:
        {
            Result = DefWindowProc(Window, Message, WParam, LParam);
        } break;
    }
    return(Result);
}


And here is the message loop.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
       if(Window)
        {
            ShowWindow(Window, CommandShow);
            UpdateWindow(Window);

            BOOL Result;
            MSG Message;
            while((Result = GetMessage(&Message, Window, 0, 0)) != 0)
            {
                if(Result == -1)
                {
                    // TODO(pete): An unknown error occured logging/message?
                    OutputDebugStringA("Are we hitting this?\n");
                    break;
                }
                else
                {
                    TranslateMessage(&Message);
                    DispatchMessage(&Message);
                }
            }
        }


If I pause the in the debug window after the window has closed, the process is halted on the
1
while((Result = GetMessage(&Message, Window, 0, 0)) != 0)
line and if I hit "step into" the process gets resumed automatically.

Is there some other thing I need to handle to be able to close the window properly from the taskbar?

Edit: I can also verify that the application recieves and handles the WM_CLOSE and WM_DESTROY messages. However I am never hitting the point where OutputDebugStringA("Are we hitting this?\n"); gets called in the message loop.

// Pete

Edited by Pete on Reason: Clarification
Remove "Window" argument from call to GetMessage, pass NULL instead.
Hey it worked!

Now just so I understand this properly. The reason I never got a WM_QUIT message was because there is some other window that I don't know about that is recieving this message instead when closing from the taskbar?
My guess is that when DestroyWindow was called in WM_CLOSE, then window handle is no more valid. I don't know exactly what GetMessage was waiting or returning, but it was nothing valid. On Windows "window messages" work in a weird way. They kind of belong to thread, not a window. By saying NULL there you are retrieving all messages that belong to this thread, including (but not limiting) all windows created by this thread.
Hmm, in terms of the order the messages gets recieved it looks pretty identical when looking at the two cases in spy++. The DestroyWindow() function gets called when pressing the close button on the top right so the window handle should be invalid in this case aswell.

Anyway thanks for the solution :)
I will just post my findings here if anyone else is curious about this in the future.

From msdn about the hWnd parameter.
A handle to the window whose messages are to be retrieved. The window must belong to the current thread.

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.


alt+f4 WM_QUIT message
- Message {msg=0x00000012 wp=0x0000000000000000 lp=0x0000000000000000} tagMSG
+ hwnd 0x0000000000000000 <NULL> HWND__ *
message 18 unsigned int
wParam 0 unsigned __int64
lParam 0 __int64
time 11141812 unsigned long
+ pt {x=637 y=765} tagPOINT


taskbar close button WM_QUIT message
- Message {msg=0x00000012 wp=0x0000000000000000 lp=0x0000000000000000} tagMSG
+ hwnd 0x0000000000000000 <NULL> HWND__ *
message 18 unsigned int
wParam 0 unsigned __int64
lParam 0 __int64
time 11168953 unsigned long
+ pt {x=525 y=1283} tagPOINT


From reading this I am now more confused about how alt+f4 and regular close button worked before the change since both messages contains NULL window handles.
Is value of "Result" the same for both cases?
Yes, the value of "Result" is 0 in both cases.
This is with NULL argument for Window? Then the loop terminates, right? But before it was not 0 in of cases, no?
Ah, so I tested again by supplying the Window parameter while((Result = GetMessage(&Message, Window, 0, 0)) != 0) and it turns out that it never handles the WM_QUIT at all (poor debugging by my part). When closing the Window using alt+f4 it exits with Result = -1, and the message is a WM_NULL message.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
+		Message,wm	{msg=WM_NULL wp=0x0000000000000000 lp=0x0000000000000000}	tagMSG

-		Message	{msg=0x00000000 wp=0x0000000000000000 lp=0x0000000000000000}	tagMSG
+		hwnd	0x0000000000000000 <NULL>	HWND__ *
		message	0	unsigned int
		wParam	0	unsigned __int64
		lParam	0	__int64
		time	0	unsigned long
+		pt	{x=0 y=0}	tagPOINT

		Result	-1	int


The error is
1
		$err,hr	ERROR_INVALID_WINDOW_HANDLE : Invalid window handle (handle). 	unsigned int


So somehow the Window gets destroyed such that the handle gets invalid if I use alt+f4 and not when using taskbar.

In both cases the DestroyWindow call gets executed and in both cases the Window handle in the WinProc is the same as the Window created in WinMain.

WinMain
1
+		Window	0x00000000000a0d48 {unused=??? }	HWND__ *

WinProc
1
+		Window	0x00000000000a0d48 {unused=??? }	HWND__ *


Also if I read the remarks on msdn about WM_QUIT more closely it says this.
The WM_QUIT message is not associated with a window and therefore will never be received through a window's window procedure. It is retrieved only by the GetMessage or PeekMessage functions.


Most of this is from me not reading carefully enough so I am thankfull for your patience :)

What I have gathered from this is that in the main window message loop you probably want to pass NULL as a window handle and then pay more attention to what window handle gets passed to the WinProc if I have a bunch of buttons etc that you want to handle outside of their specific WinProcs somehow. Or I have multiple message loops but this requires additional threads?

Edit:
From MSDN about hWnd parameter in GetMessage().
If hWnd is -1, GetMessage retrieves only messages on the current thread's message queue whose hwnd value is NULL, that is, thread messages as posted by PostMessage (when the hWnd parameter is NULL) or PostThreadMessage.


So if you wanted to handle this in a seperate loop you would have one for the thread and pass -1 as hWnd. Guess it's always in the details.

Edited by Pete on
Technically you probably can do multiple threads for multiple windows, but that can get very messy very fast. Not many people do that.
Typically you handle responses to window controls inside window message callback. Very rarely (almost never) you do something outside, unless it is super simple application.