Differencebetween Wm_Quit, Wm_Close, and Wm_Destroy in a Windows Program

What is the difference between WM_QUIT, WM_CLOSE, and WM_DESTROY in a windows program?

They are totally different.

WM_CLOSE is sent to the window when it is being closed - when its "X" button is clicked, or "Close" is chosen from the window's menu, or Alt-F4 is pressed while the window has focus, etc. If you catch this message, this is your decision how to treat it - ignore it, or really close the window. By default, WM_CLOSE passed to DefWindowProc() causes the window to be destroyed.

WM_DESTROY is sent to the window when it starts to be destroyed. In this stage, in opposition to WM_CLOSE, you cannot stop the process, you can only make any necessary cleanup. When you catch WM_DESTROY, none of its child windows have been destroyed yet.

WM_NCDESTROY is sent to the window when it is finishing being destroyed. All of its child windows have been destroyed by this time.

WM_QUIT is not related to any window (the hwnd got from GetMessage() is NULL, and no window procedure is called). This message indicates that the message loop should be stopped and the application should exit. When GetMessage() reads WM_QUIT, it returns 0 to indicate that. Take a look at a typical message loop snippet - the loop is continued while GetMessage() returns non-zero.

WM_QUIT can be sent by the PostQuitMessage() function. This function is usually called when the main window receives WM_DESTROY (see a typical window procedure snippet).

What is the behavior WM_CLOSE and WM_QUIT messages in VB6?

WM_CLOSE asks the window to close. Most apps will exit after closing its main window.

WM_QUIT tells the window to quit the current message loop. If that's the main message loop, most apps will then exit. If it's a message loop for a modal dialog, the dialog will likely close and the app will likely continue to run.

Why is there a difference in the order WM_DESTROY is received between child and owned windows?

The official documentation for DestroyWindow() says otherwise:

If the specified window is a parent or owner window, DestroyWindow automatically destroys the associated child or owned windows when it destroys the parent or owner window. The function first destroys child or owned windows, and then it destroys the parent or owner window.

Are you sending WM_DESTROY by hand instead of calling DestroyWindow()?

What's the logical difference between PostQuitMessage() and DestroyWindow()?

DestroyWindow destroys the window (surprise) and posts a WM_DESTROY (you'll also get a WM_NCDESTROY) to the message queue. This is the default behaviour of WM_CLOSE. However, just because a window was destroyed does not mean the message loop should end. This can be the case for having a specific window that ends the application when closed and others that do nothing to the application when closed (e.g., an options page).

PostQuitMessage posts a WM_QUIT to the message queue, often causing the message loop to end. For example, GetMessage will return 0 when it pulls a WM_QUIT out. This would usually be called in the WM_DESTROY handler for your main window. This is not default behaviour; you have to do it yourself.

PostQuitMessage(WM_QUIT) vs PostQuitMessage(0)

When you call PostQuitMessage, you pass an exit code, not a message ID. PostQuitMessage will in turn generate (and post) the WM_QUIT message for you.

So technically, the difference is that with PostQuitMessage(WM_QUIT), the exit code will be 0x0012 (or 18 in decimal). Whereas PostQuitMessage(0) will provide an exit code of 0.

When either GetMessage and PeekMessage see a WM_QUIT message, they will return zero, and you can check for the exit code in the WPARAM part of the LPMSG parameter.

To return the exit code (the value you pass to PostQuitMessage) the message pump for your application could be something like this:

MSG msg;
while (0 != GetMessage(&msg, 0, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return msg.wParam;

the WM_CLOSE event is never sent/received?

Thanks to Simon Mourier comment and link to a tutorial, the problem was solved.

The message handling had to be done in WndProc, not in the "main loop".

I'm reposting the modified, cleaned, working, code :

#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;
}

Does PostQuitMessage() goes into WM_DESTROY or WM_CLOSE?

Its your GetMessage() loop.

You're passing your window handle to that loop, thereby filtering out all application-thread messages and only receiving messages to that window. .

Change this:

while ((ret = GetMessage(&msg, hwnd, 0, 0)) != 0) 
// Note window handle =========^

To this:

while ((ret = GetMessage(&msg, NULL, 0, 0)) != 0) 
// Note no handle =============^

And your application thread queue should now be monitored by your GetMessage() loop.

Why: GetMessage() invokes can be tailored to monitor a specific window handle's message queue. The application WM_QUIT is not posted to a window handle queue; it is posted to the thread-message queue, which can only pull messages off the queue by using GetMessage() (perhaps PeekMessage() as well, but its been too long for me to remember) with no target window handle to specifically monitor.

WM_DESTROY, WM_CLOSE bypassing IMessageFilter

IMessageFilter.PreFilterMessage() is only called for messages in the message queue. Messages like WM_CLOSE are sent directly to WndProc() with SendMessage(), they bypass the queue. You also won't get messages like WM_ACTIVATE, WM_GETTEXT, etc. Input events, that's about it.

Message loop not exiting when I close the window

By providing window as an argument to PeekMessage you are telling it you only want to retrieve messages posted or sent to that window.

But WM_QUIT is a thread message - it's not associated with any given window. To retrieve it you need to call PeekMessage with nullptr for the window filter.



Related Topics



Leave a reply



Submit