should WndProc process WM_QUIT?

Started by
8 comments, last by SOL-2517 8 years, 10 months ago

two questions:

1. should WndProc process WM_QUIT?

some sources seem to indicate yes, others, no.

i currently do not.

for WM_DESTROY i call postquitmessage(0), which apparently sends a WM_QUIT to windows to unhook the wndproc.

for WM_CLOSE i use the default processing of DefWndProc, so i don't process it myself, just pass it on. DefWndProc in turn apparently sends me back a WM_DESTROY, which i then process.

but i've also heard that once i process WM_DESTROY and postquitmessage, that i should then wait until i get a WM_QUIT back before i actually terminate.

2. when i do determine inside wndproc that i should terminate, due to a WM_DESTROY (or possibly WM_QUIT) being received, whats the best way to tell the app to quit, given that i could be anywhere in the code that i run the windows message pump? since WndProc is a callback, the only way i see is to set some sort of "time to die" flag in WndProc, then check it in the main loop (perhaps at the end of an update cycle), and terminate if its set to true. but that seems to imply use of a global variable, IE some ram dedicated to data exchange between the app and callback.

is this why its a good idea to have just one message pump loop, and then have the whole rest of the program, which runs when the loop isn't busy? and if you get a message to terminate, you just postquitmessage, exit the windows pump loop, then terminate? the natural flow control of the code precludes the need for a flag?

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

Advertisement

1. No.

https://msdn.microsoft.com/en-us/library/windows/desktop/ms632641%28v=vs.85%29.aspx

When GetMessage returns that it has received a WM_QUIT, your window will already be destroyed.

2. As WM_QUIT is detected in your main loop and not in the WndProc this isn't an issue, just exit when GetMessage returns 0. See docs for GetMessage and PeekMessage for details.

Erik hit the nail on the head. I never bother detecting or processing "WM_QUIT." "WM_DESTROY" will suffice for just about anything you would need it for.

"The code you write when you learn a new language is shit.
You either already know that and you are wise, or you don’t realize it for many years and you are an idiot. Either way, your learning code is objectively shit." - L. Spiro

"This is called programming. The art of typing shit into an editor/IDE is not programming, it's basically data entry. The part that makes a programmer a programmer is their problem solving skills." - Serapth

"The 'friend' relationship in c++ is the tightest coupling you can give two objects. Friends can reach out and touch your privates." - frob

To expand a bit on WM_QUIT detection: if you use PeekMessage (vs GetMessage) in your main loop, unlike GetMessage which returns 0 if the message is WM_QUIT, PeekMessage will return TRUE (-1) when it retrieves the WM_QUIT message. Simply test whether the retrieved msg.message == WM_QUIT. If so, simply exit your application gracefully.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.


if you use PeekMessage (vs GetMessage) in your main loop, unlike GetMessage which returns 0 if the message is WM_QUIT, PeekMessage will return TRUE (-1) when it retrieves the WM_QUIT message. Simply test whether the retrieved msg.message == WM_QUIT. If so, simply exit your application gracefully.

so in the WndProc, on WM_DESTROY, I postquitmessage. and in the messagepump, i also have to sniff for WM_QUIT and halt when i get it, not on WM_DESTROY. thanks, that clears things up a bit. didn't come across any real good examples of that online.

there's no real harm in halting in the message pump on WM_QUIT is there? you're not halting in the winproc, your window has been destroyed, and your winproc has been unhooked. shut things down, time to die. right?

funny thing is this code only get executed if they kill the game from the desktop. if they quit from the main menu in the game, i just shutdown, postquitmessage, and return(0) from WinMain. the game is fullscreen only, so no close button.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php


shut things down, time to die. right?

Yep.


if they quit from the main menu in the game, i just shutdown, postquitmessage ...

That shutdown process needn't be different - you can just call DestroyWindow (which posts WM_DESTROY) - and the same sequence would be run through.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

funny thing is this code only get executed if they kill the game from the desktop. if they quit from the main menu in the game, i just shutdown, postquitmessage, and return(0) from WinMain. the game is fullscreen only, so no close button.


What do you mean "kill game from the desktop"? If they're just closing the window via the right-click menu you should still get close messages. If they're going into task manager and hitting End Task I believe windows sends you a WM_QUIT directly to give you a chance to exit cleanly (and if you don't respond to that, or they try to kill your process you don't get anything at all, you just cease to exist).

Little note on WM_QUIT: If you have more than one message pumps going (different threads); or nested pumps (modal windows for example), you may have to repost the WM_QUIT message if you receive it in one of the inner loops.

Mr. Chen says this: http://blogs.msdn.com/b/oldnewthing/archive/2005/02/22/378018.aspx

Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>


That shutdown process needn't be different - you can just call DestroyWindow (which posts WM_DESTROY) - and the same sequence would be run through.

instead of running the message pump, then running a frame when there are no messages, i run it when i need mouse input. so it gets called once in get_input, and in popup menus etc. to quit by using destroywindow, i'd have to call destroywindow then run the message pump and wait for WM_QUIT to show up. which strikes me as unnecessary, due to my atypical message pump usage pattern.


What do you mean "kill game from the desktop"?

fullscreen only app. the only way to quit is via "quit" on the main menu, or alt-tab or ctrl-alt-del out of the game, then close the game from the desktop via taskbar close window or program manager end process. if they do this, i figure they don't care about saving first <g>.


you may have to repost the WM_QUIT message if you receive it in one of the inner loops.

yes, i came across that with modal pumps. fortunately i have just one pump, but call it from many places. same idea as modal, but all pumps just happen to be identical in behavior.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

so in the WndProc, on WM_DESTROY, I postquitmessage. and in the messagepump, i also have to sniff for WM_QUIT and halt when i get it, not on WM_DESTROY. thanks, that clears things up a bit. didn't come across any real good examples of that online.

That's one way to do it. I prefer to not use WM_DESTROY and PostQuitMessage at all and instead use the window data (accessed with SetWindowLongPtr/GetWindowLongPtr) to determine when to quit. This is in my opinion the best way to do it if you're writing an engine that should support multiple windows since the window procedure doesn't need to keep track of when the application should quit. This way the user of the engine is completely in control of when the window should be closed and destroyed. That's how GLFW does it:


while (!glfwWindowShouldClose(window))
{
    ...
    glfwPollEvents();
    ...
}

glfwDestroyWindow(window);

If you want to implement this yourself then you can do something like this (I've left out all the error checking etc to make it easier to read):


// File: window.h
typedef struct FOOAPI_Window;

// File: window.c
#include <FOOAPI/window.h>

struct FOOAPI_Window
{
    HWND window_handle;
    bool should_close;
} FOOAPI_Window;

FOOAPI_Window *FOOAPI_CreateWindow(...)
{
    FOOAPI_Window *window = malloc(sizeof(FOOAPI_Window));
    window->window_handle = CreateWindow(...); // Win32 API
    window->should_close = false;

    SetWindowLongPtr(window->window_handle, GWLP_USERDATA, (LONG)window); // Win32 API
    return window;
}

void FOOAPI_DestroyWindow(FOOAPI_Window *window)
{
    DestroyWindow(window->window_handle); // Win32 API
    free(window);
}

void FOOAPI_SetWindowShouldClose(FOOAPI_Window *window)
{
    window->should_close = true;
}

bool FOOAPI_WindowShouldClose(FOOAPI_Window *window)
{
    return window->should_close;
}

static LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    FOOAPI_Window *window;

    switch (message)
    {
    case WM_CLOSE:
        window = (FOOAPI_Window *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
        window->should_close = true;
        return 0;
    }

    return DefWindowProc(hwnd, message, wParam, lParam);
}

This topic is closed to new replies.

Advertisement