flat assembler
Message board for the users of flat assembler.

Index > High Level Languages > [solv]winapi, C, why process stays after I close the window?

Author
Thread Post new topic Reply to topic
vivik



Joined: 29 Oct 2016
Posts: 671
vivik 23 Dec 2016, 16:25
Look at this program, maybe you know why GetMessage doesn't return 0? It should return 0 after WM_QUIT, but still, process stays up forever, until I close it from Task Manager.

Code:
#include <windows.h>

LRESULT CALLBACK
WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);


int main() {
    window_create();
}

void window_create()
{
    WNDCLASS    wc;

    wc.style         = CS_OWNDC;
    wc.lpfnWndProc   = (WNDPROC)WndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = 0x400000;
    wc.hIcon         = LoadIcon(NULL, IDI_WINLOGO);
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = NULL;
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = "TheClass";

    RegisterClass(&wc);

    HWND hwnd;

    hwnd = CreateWindowA("TheClass", "TheTitle", WS_OVERLAPPEDWINDOW |
                         WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE,
                         100, 100, 600, 400, NULL, NULL, 0x400000, NULL);

    MSG   msg;

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

    ExitProcess(0);
}



LRESULT CALLBACK
WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{

    switch (message) {

    default:
        return DefWindowProc(hwnd, message, wParam, lParam);
    }
    return 0;
}
    


I really want to learn winapi already.


Last edited by vivik on 23 Dec 2016, 16:40; edited 1 time in total
Post 23 Dec 2016, 16:25
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20451
Location: In your JS exploiting you and your system
revolution 23 Dec 2016, 16:31
I don't know if this note on the docs is related or not, but here it is:
The MS docs wrote:
Note that the function return value can be TRUE, FALSE, or -1. Thus, you should avoid code like this:

while (GetMessage( lpMsg, hWnd, 0, 0)) ...


The possibility of a -1 return value means that such code can lead to fatal application errors.
Post 23 Dec 2016, 16:31
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20451
Location: In your JS exploiting you and your system
revolution 23 Dec 2016, 16:34
Also: Your problem is probably because you never call PostQuitMessage :
Quote:
The WM_QUIT message indicates a request to terminate an application and is generated when the application calls the PostQuitMessage function.
Post 23 Dec 2016, 16:34
View user's profile Send private message Visit poster's website Reply with quote
vivik



Joined: 29 Oct 2016
Posts: 671
vivik 23 Dec 2016, 16:39
Yeah, last one was it! I added this:

Code:
        case WM_CLOSE:
                PostQuitMessage(0);
                return 0;
    


into WndProc, and it finally leaves that loop. Hope it's the correct way.

Thanks!
Post 23 Dec 2016, 16:39
View user's profile Send private message Reply with quote
DimonSoft



Joined: 03 Mar 2010
Posts: 1228
Location: Belarus
DimonSoft 25 Dec 2016, 20:55
vivik wrote:
Yeah, last one was it! I added this:

Code:
        case WM_CLOSE:
                PostQuitMessage(0);
                return 0;
    


into WndProc, and it finally leaves that loop. Hope it's the correct way.

Thanks!

I’m definitely not the only one to know this, but I think I’ll add this comment for those who may come here with the same question: whether it is the correct way.

The answer for this piece of code (as well as similar ones) is YES. The message loop’s condition depends on the return value of GetMessage(). As previously mentioned, it may return TRUE, FALSE and –1. The case of –1 is mostly irrelevant (it may be caused by either wrong parameters or some serious consistency problems). Normally the return values are TRUE and FALSE, and they basically answer the question “Was the message retrieved during the call NOT a WM_QUIT message?”

To exit the loop you need to make GetMessage() return FALSE. Thus, you must make it retrieve a WM_QUIT message from the message queue. Which can be done by PostQuitMessage() function, i.e. putting the WM_QUIT message into the message queue.
Post 25 Dec 2016, 20:55
View user's profile Send private message Visit poster's website Reply with quote
AsmGuru62



Joined: 28 Jan 2004
Posts: 1671
Location: Toronto, Canada
AsmGuru62 25 Dec 2016, 23:05
It is better to call PostQuitMessage() in response to WM_DESTROY.
WM_CLOSE is for cleaning up things when application closes, like saving documents, etc.
Also, you can decide not to close the program in response to WM_CLOSE.
Post 25 Dec 2016, 23:05
View user's profile Send private message Send e-mail Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20451
Location: In your JS exploiting you and your system
revolution 26 Dec 2016, 01:27
One thing to note that WM_CLOSE and WM_DESTROY are meant to apply only to the window associated with HWND. It is not meant to imply that the entire application closes. Some applications have multiple open windows, some can run in the background when the last open window is closed. So make sure it makes sense in your application to post the quit message when a window is closed or destroyed. That is, don't simply do it because some sample code on the Internet does it.
DimonSoft wrote:
The case of –1 is mostly irrelevant ...
... until it becomes relevant when you are trying to find out why your app just crashed for no apparent reason.
Post 26 Dec 2016, 01:27
View user's profile Send private message Visit poster's website Reply with quote
DimonSoft



Joined: 03 Mar 2010
Posts: 1228
Location: Belarus
DimonSoft 29 Dec 2016, 21:34
revolution wrote:
DimonSoft wrote:
The case of –1 is mostly irrelevant ...
... until it becomes relevant when you are trying to find out why your app just crashed for no apparent reason.

Well, I wouldn’t write this if I weren’t sure it was a safe assumption. I’m referring here to Raymond Chen’s blog post: https://blogs.msdn.microsoft.com/oldnewthing/20130322-00/?p=4873

If GetMessage(pMsg, 0, 0, 0) returns –1 for some other reason than wrong parameters, then either Microsoft screwed up the backwards compatibility making wrong millions of applications out there, or this happened due to the corruption of some internal structures, in which case the problem is not with the call to GetMessage() anyway.

Although writing code with such assumptions is a bad idea in general, in this case it’s safe enough.
Post 29 Dec 2016, 21:34
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20451
Location: In your JS exploiting you and your system
revolution 29 Dec 2016, 21:42
DimonSoft wrote:
Well, I wouldn’t write this if I weren’t sure it was a safe assumption. I’m referring here to Raymond Chen’s blog post: https://blogs.msdn.microsoft.com/oldnewthing/20130322-00/?p=4873

If GetMessage(pMsg, 0, 0, 0) returns –1 for some other reason than wrong parameters, then either Microsoft screwed up the backwards compatibility making wrong millions of applications out there, or this happened due to the corruption of some internal structures, in which case the problem is not with the call to GetMessage() anyway.

Although writing code with such assumptions is a bad idea in general, in this case it’s safe enough.
The problem I foresee is that it becomes a source of error when things change but the reasoning behind it was forgotten. So in two years time when you copy the code into a new app you are writing (and totally forget about the third boolean result code), and then change a parameter in the call because the new app has different requirements and suddenly ... CRASH RANDOMLY OMG!!!

Besides you probably spent more time researching some form of defence and justification for writing the buggy code than it would have taken to just write the code "correctly" in the first place. It only takes, what, two or three more lines of code to make it bullet proof, so why the reluctance?
Post 29 Dec 2016, 21:42
View user's profile Send private message Visit poster's website Reply with quote
Trinitek



Joined: 06 Nov 2011
Posts: 257
Trinitek 29 Dec 2016, 23:58
revolution wrote:
DimonSoft wrote:
Well, I wouldn’t write this if I weren’t sure it was a safe assumption. I’m referring here to Raymond Chen’s blog post: https://blogs.msdn.microsoft.com/oldnewthing/20130322-00/?p=4873

If GetMessage(pMsg, 0, 0, 0) returns –1 for some other reason than wrong parameters, then either Microsoft screwed up the backwards compatibility making wrong millions of applications out there, or this happened due to the corruption of some internal structures, in which case the problem is not with the call to GetMessage() anyway.

Although writing code with such assumptions is a bad idea in general, in this case it’s safe enough.
The problem I foresee is that it becomes a source of error when things change but the reasoning behind it was forgotten. So in two years time when you copy the code into a new app you are writing (and totally forget about the third boolean result code), and then change a parameter in the call because the new app has different requirements and suddenly ... CRASH RANDOMLY OMG!!!
That's what comments are for.
Post 29 Dec 2016, 23:58
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20451
Location: In your JS exploiting you and your system
revolution 30 Dec 2016, 04:31
Nobody reads comments unless something goes wrong. Besides it would take you just as long, and probably much longer, to write the comments explaining why your code is broken and what to do if you change the code in the future.

ETA: I've seen this attitude in my colleagues in the past, and in myself also. And now it makes me cringe to see code left deliberately broken with various justifications for leaving it broken.

Don't write broken code and then waste time documenting the brokenness, enumerating things that trigger the breaking, and create potential headaches for yourself and others that want to use the code. Just fix it and move on, no need to worry about it any more.
Post 30 Dec 2016, 04:31
View user's profile Send private message Visit poster's website Reply with quote
DimonSoft



Joined: 03 Mar 2010
Posts: 1228
Location: Belarus
DimonSoft 04 Jan 2017, 08:28
revolution wrote:
The problem I foresee is that it becomes a source of error when things change but the reasoning behind it was forgotten. So in two years time when you copy the code into a new app you are writing (and totally forget about the third boolean result code), and then change a parameter in the call because the new app has different requirements and suddenly ... CRASH RANDOMLY OMG!!!

Besides you probably spent more time researching some form of defence and justification for writing the buggy code than it would have taken to just write the code "correctly" in the first place. It only takes, what, two or three more lines of code to make it bullet proof, so why the reluctance?

OK, first of all, I really understand your point of not relying on implementation details of third-party code.

Also, note that the “irrelevance” of –1 return value was originally related to the explanation of the logic behind GetMessage()-based message loop and the usage of PostQuitMessage(). While your nitpicking is definitely useful for the purpose of discussion, it gives your nothing while talking about whether PostQuitMessage() is the right way to exit the loop.

Now, back to our nitpicky discussion.

Your suggestion about the case of changing the requirements is great… and quite pointless. Any case of non-null parameters to GetMessage() is already a requirement to pay special attention to that piece of code, since this is almost always (except very specific scenarios) a terrible idea.

Since (unlike, say, CreateFile) GetMessage() merely moves a few pieces of data in memory, all the failures can be tracked down to:
* some sort of memory corruption — caused by some other code;
* some logically wrong request — you’d better avoid the request in the first place instead of checking its result.

While the first reason is not a real GetMessage() failure, the second one may occur if you either screw up with message ranges or invalidate the window handle — due to wrong parameters.

There are lots of cases when you can do relatively nothing to handle an error. CloseHandle() sometimes fails, BitBlt() sometimes fails… And they all return the error codes. The error codes which are mostly for debugging purposes.

What handling for GetMessage() failure would you suggest? I can think of only two valid things off the top of my head:
* terminate;
* ignore.

OK, I agree that you’d better terminate. But millions of programs don’t.
1) Because they were written when GetMessage() returned BOOL.
2) Because it uses some library that ignores the failure (possibly due to reason 1).
3) Because the programmer wasn’t aware of the case.

The third reason is bad, but the first two reasons are valid and those programs don’t expose some wrong behaviour. Why? Because you shouldn’t allow “wrong parameters” errors get into production code anyway. They provide valid parameters (all-nulls in most cases) and that’s it.

About “CRASH RANDOMLY OMG!!!”…

First of all, it wouldn’t be random. If you ignore GetMessage() failure, you just don’t exit the message loop. So, speaking in terms of MVC, your controllers (and subsequently your models) just get messages in an unexpected order (probably don’t get some of them). If all the message handlers leave your data consistent (as they MUST), the only reason for a crash is some uninitialized member in case of lazy initialization causing a NULL pointer dereferencing, which is easily debuggable by inspecting the requests to the model.

But!.. I claim that crashing is GOOD. It is much better than working with corrupted data and extending the corruption further. If the program crashes, you’re lucky: you now know there’s a problem. In the worst case, when the program doesn’t crash, you start corrupting the user’s data. So, no place for “OMG” here.

I understand the reasoning behind checking for GetMessage() failures in new programs (which I also do, except the cases of make-it-tiniest-possible projects), but what I’m trying to say is that it’s not so urgent.

In practice, knowing the story behind GetMessage(), I’d compare handling its failures to handling exceptions while executing each mov: it definitely makes your code prepared to errors, but doesn’t make much sense. It would rather be a better idea to improve the overall code quality than to have a code branch that you’ll never hit in production and that can’t even be properly tested. IMHO. But we have what we have.
Post 04 Jan 2017, 08:28
View user's profile Send private message Visit poster's website Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2025, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.