News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

How to exit a windows programme

Started by jj2007, February 02, 2009, 09:18:37 AM

Previous topic - Next topic

jj2007

Two newbie questions:
1. Is the return 0 after PostQuitMessage useful? I.e. does it any harm to let Windows continue its default processing?
2. Is there any useful application of a DEFAULT case you could think of?

...
invoke WinMain, hInstance, 0, 0, SW_SHOWDEFAULT
MsgBox 0, "Bye", offset AppName, MB_OK ; test if your code exists properly
invoke ExitProcess, 0

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
... code ...
CASE WM_DESTROY
invoke PostQuitMessage, NULL
; return 0 ; *** is this needed? ***
ENDSW
invoke DefWindowProc, hWnd, uMsg, wParam, lParam
ret
WndProc endp

gwapo

Quote from: jj2007 on February 02, 2009, 09:18:37 AM
1. Is the return 0 after PostQuitMessage useful? I.e. does it any harm to let Windows continue its default processing?

The argument you pass to PostQuitMessage is just an exit code (wparam of WM_QUIT message).
It can be anything, but we usually set it to NULL or 0 to indicate successful execution.

The exit code is simply a return value (much like the return of a function) for calling process.


Quote from: jj2007 on February 02, 2009, 09:18:37 AM
2. Is there any useful application of a DEFAULT case you could think of?

You mean DefWindowProc?


Quote from: jj2007 on February 02, 2009, 09:18:37 AM
...
invoke WinMain, hInstance, 0, 0, SW_SHOWDEFAULT
MsgBox 0, "Bye", offset AppName, MB_OK ; test if your code exists properly
invoke ExitProcess, 0

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
... code ...
CASE WM_DESTROY
invoke PostQuitMessage, NULL
; return 0 ; *** is this needed? ***
ENDSW
invoke DefWindowProc, hWnd, uMsg, wParam, lParam
ret
WndProc endp


No, you don't need to `return' after you've posted a quit message as a response to WM_DESTROY notification, so DefWindowProc can take care of other things you didn't handled in your code, like WM_NCDESTROY or other "destroy window" related notifications, and sending destroy message to all other active windows within the same thread, etc.

HTH

-chris

ragdogz

Hi, count me in on those newbie questions  :green

Quote from: gwapo on February 02, 2009, 10:17:42 AM
so DefWindowProc can take care of other things you didn't handled in your code, like WM_NCDESTROY or other "destroy window" related notifications, and sending destroy message to all other active windows within the same thread, etc.

HTH

-chris

i'm using a dialog box as a main window of my program. how to "completely destroy" like that?
is this code enough?

.elseif uMsg==WM_CLOSE
invoke EndDialog,hWnd,NULL
invoke ExitProcess,0


thx..

jj2007

Quote from: gwapo on February 02, 2009, 10:17:42 AM
No, you don't need to `return' after you've posted a quit message as a response to WM_DESTROY notification, so DefWindowProc can take care of other things you didn't handled in your code, like WM_NCDESTROY or other "destroy window" related notifications, and sending destroy message to all other active windows within the same thread, etc.

-chris

Thanks for clarifying this, Chris. I had a suspicion that Windows should not be blocked right there to do certain cleanup tasks.
Maybe it would be good to correct at least \masm32\examples\exampl01\generic\generic.asm...

    .elseif uMsg == WM_DESTROY
    ; ----------------------------------------------------------------
    ; This message MUST be processed to cleanly exit the application.
    ; Calling the PostQuitMessage() function makes the GetMessage()
    ; function in the WinMain() main loop return ZERO which exits the
    ; application correctly. If this message is not processed properly
    ; the window disappears but the code is left in memory.
    ; ----------------------------------------------------------------
        invoke PostQuitMessage,NULL
        return 0
    .endif

    invoke DefWindowProc,hWin,uMsg,wParam,lParam

MichaelW

I see nothing wrong with the comments in generic.asm. The WM_DESTROY message must be processed and PostQuitMessage must be called, or the application will not exit correctly. You can verify this by building the app without the call to PostQuitMessage and observing it in Task Manager when you attempt to close it. And per the PSDK for the WM_DESTROY notification:
QuoteIf an application processes this message, it should return zero.

So that part is "by the book", even if it's not necessary.
eschew obfuscation

Tedd

The code is correct - so it doesn't need fixing :P

For any message you handle, you should return the correct value to indicate you handled it, otherwise pass it on to DefWindowProc (and it will handle it) and return whatever value it returns. WM_DESTORY is just another message for you to handle (or not.) All PostQuitMessage does it put a WM_QUIT onto the end of your message queue and return - any other messages still in your queue will still be processed afterwards.
Anyway, you 'should' return zero to say you handled it, though the world won't end if you don't.. if you allow it to fall through to DefWindowProc, I expect it will simply do exactly the same (call PostQuitMessage, and return zero) but in that case, there's no need for you to handle the message at all.


If you're running a Dialog as the main window, you should still return as normal, and allow the 'caller' (of DialogBox/Param) to ExitProcess -- you shouldn't ExitProcess from within handling a message, there may still be other messages waiting to be handled.
No snowflake in an avalanche feels responsible.

jj2007

Quote from: Tedd on February 02, 2009, 07:02:27 PM
if you allow it to fall through to DefWindowProc, I expect it will simply do exactly the same (call PostQuitMessage, and return zero) but in that case, there's no need for you to handle the message at all.

If you don't handle the message, it will not exit properly.

The open question is, does the DefWindowProc anything useful if you allow it to run? Chris mentionend ncdestroy...

jj2007

I did some more testing...

invoke WinMain, hInstance, 0, 0, SW_SHOWDEFAULT
MsgBox 0, "Bye", offset AppName, MB_OK ; uncomment to test if your code exists properly
invoke ExitProcess, eax

WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:DWORD
...
CASE WM_CLOSE
MsgBox 0, "Close", "Hi", MB_OK
if 0 ; no difference in behaviour
invoke DestroyWindow, hWnd ; will destroy all its children, too
invoke FreeLibrary, hLib ; get rid of RichEd20.dll
invoke DestroyAcceleratorTable, hAccT ; and the accelerators
endif

CASE WM_QUIT
MsgBox 0, "Quit", "Hi", MB_OK ; NEVER seen

CASE WM_DESTROY
MsgBox 0, "Destroy", "Hi", MB_OK
; no good here:
; invoke FreeLibrary, hLib
; invoke DestroyAcceleratorTable, hAccT
invoke PostQuitMessage, NULL
; return 0 ; no difference in behaviour
ENDSW
invoke DefWindowProc, hWnd, uMsg, wParam, lParam
ret
WndProc endp

Tedd

Quote from: jj2007 on February 02, 2009, 08:07:16 PM
Quote from: Tedd on February 02, 2009, 07:02:27 PM
if you allow it to fall through to DefWindowProc, I expect it will simply do exactly the same (call PostQuitMessage, and return zero) but in that case, there's no need for you to handle the message at all.
If you don't handle the message, it will not exit properly.
Yeah, I was unable to actually test it just then :wink

Quote
The open question is, does the DefWindowProc anything useful if you allow it to run? Chris mentionend ncdestroy...
wm_ncdestroy is another separate message, so it will be sent separately -- there is actually a predefined order that these 'closing' messages are sent to a window (I have read it somewhere - probably msdn; or you can try printing each received message to a debug-window/file and sift through that.)


WM_CLOSE is a warning the user attempted to close the application (usually the X button) allowing you to ask to save/cancel/etc - you shouldn't really 'cleanup' there, just abort, or continue the self-destruct sequence (with DestroyWindow)
WM_DESTROY is then your 'final warning' before the window is completely destroyed - this is where you should do the final cleanup, and PostQuitMessage
WM_QUIT will never be received - your window is destroyed by then, so it can't receive any messages. For that reason it's not a 'real' message, but you 'receive' it in the form of the GetMessage returning zero to indicate no more messages, which is usually when you exit your message-loop.


Your code shows no apparent difference in behaviour because it falls to DefWindowProc after WM_DESTROY, which acts sensibly and returns zero - but that doesn't mean it's the correct behaviour, only that it's not entirely wrong (in the same way as not freeing allocated memory - ExitProcess will usually clean it up, but that doesn't mean it's right or that you should.)
No snowflake in an avalanche feels responsible.

sinsi

I always thought that WM_DESTROY and PostQuitMessage don't always go together - if you create another window (e.g. an 'about' window) then you don't want
to use PostQuitMessage in that window's WM_DESTROY handler.
Light travels faster than sound, that's why some people seem bright until you hear them.

gwapo

Quote from: jj2007 on February 02, 2009, 08:07:16 PM
Quote from: Tedd on February 02, 2009, 07:02:27 PM
if you allow it to fall through to DefWindowProc, I expect it will simply do exactly the same (call PostQuitMessage, and return zero) but in that case, there's no need for you to handle the message at all.

If you don't handle the message, it will not exit properly.

The open question is, does the DefWindowProc anything useful if you allow it to run? Chris mentionend ncdestroy...

Depending on how you constructed your message loop, here's an example:


WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
invoke DefWindowProc, hWnd, uMsg, wParam, lParam  ; DefWindowProc processes dialog related messages (because of Is
ret
WndProc endp


The window procedure above doesn't process any window (dialog) messages, but as soon as you press the X close button (assuming empty window with close button), it will then let DefWindowProc (or if you have IsDialogMessage attached in your message loop) post the quit message, which will then be used to terminate your message loop.

Here's how I made the message loop for doing that:

func macro functionName:req, functionParams:vararg
invoke functionName, functionParams
exitm <eax>
endm

        :
        :
        :
        :

.while TRUE
invoke GetMessage, addr msg, hwnd, 0, 0

; if WM_QUIT was posted, then GetMessage will return 0 (you won't actually get WM_QUIT message here,
                ; the value in msg.message will be zero as well, not WM_QUIT or 12h)
.break .if eax!=TRUE

; Let IsDialogMessage perform dialog related message processing for us (e.g., WM_CLOSE)
.if !func(IsDialogMessage,hwnd,addr msg)
invoke TranslateMessage, addr msg
invoke DispatchMessage, addr msg
.endif
.endw

        ; If you sent an exit code using PostQuitMessage, then msg.wParam will have that exit code...
        mov eax, msg.wParam
ret


Anyway, my apology for hasty response the last time. I didn't noticed you were asking about WM_DESTROY message to post a quit message
(I thought you were asking about WM_CLOSE/PostQuitMessage combination). The code is actually correct [again, my mistake], there's no real need to let it
fall through DefWindowProc. Return 0 just do fine, bacause there's nothing left for DefWindowProc to process on window that's already being destroyed.

As for WM_QUIT, your window procedure will never receive WM_QUIT notification, this notification is for GetMessage function
to use internally (think of it like a NULL terminator in strings, GetMessage uses WM_QUIT as messages terminator in the messages pool).


WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

; Destroy the window
.if uMsg==WM_DESTROY
invoke PostQuitMessage, 0 ; This is just a redundancy (call PostQuitMessage if you need to post non-zero exit code)
return 0  ; No difference in behaviour

; You will never receive this message
.elseif uMsg==WM_QUIT ; not needed

.endif

invoke DefWindowProc, hWnd, uMsg, wParam, lParam
ret
WndProc endp


Then, WM_CLOSE notification is more like trying to make a confirmation for the request to close the window, you will receive this for every attempt
to close the window (e.g., clicked close button, received Alt-F4 key combination, trying to end the process in task explorer, etc). Letting it fall through
DefWindowProc will destroy the window (hence WM_DESTROY notification will be sent next):


WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

                :
                :
                :
                :

; Window has a request to close -- exit the procedure if you don't want to close just yet,
        ; otherwise, just let it fall through DefWindowProc
.elseif uMsg==WM_CLOSE
mov eax, appStatus
.if eax==BUSY_STILL
return 1 ; I don't want to exit yet
.endif
.endif

invoke DefWindowProc, hWnd, uMsg, wParam, lParam
ret
WndProc endp


Or you may want to close it yourself by posting quit message as a response to WM_CLOSE
and your window procedure won't receive WM_DESTROY notification if you didn't let it fall
through DefWindowProc:


WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

; You won't receive this notification if WM_CLOSE didn't let it fall through DefWindowProc
.if uMsg==WM_DESTROY

                :
                :
                :
                :

; Post quit message as a response to WM_CLOSE
.elseif uMsg==WM_CLOSE
invoke PostQuitMessage, 0 ; quit without destroying the window
                return 0 ; Don't let DefWindowProc post WM_DESTROY notification...
.endif

invoke DefWindowProc, hWnd, uMsg, wParam, lParam
ret
WndProc endp


Finally, WM_NCDESTROY - You will receive this right after WM_DESTROY notification. This notification is mostly used by child windows with heavy tasks being performed in non-client area (e.g., text editors with line numbering).


WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

.if uMsg==WM_DESTROY
                invoke PostQuitMessage, 0
                return 0 ; child controls won't receive WM_NCDESTROY

invoke DefWindowProc, hWnd, uMsg, wParam, lParam
ret
WndProc endp


So there, hopefully, that's more helpful than my previous hasty comment.

Cheers,

-chris

gwapo

Quote from: sinsi on February 02, 2009, 10:56:23 PM
I always thought that WM_DESTROY and PostQuitMessage don't always go together - if you create another window (e.g. an 'about' window) then you don't want
to use PostQuitMessage in that window's WM_DESTROY handler.

Yes you are right, because PostQuitMessage will terminate the message loop, so don't post quit message in child windows (or other dialogs other than the main window). In your example, you don't actually need to handle WM_DESTROY in AboutBox, just destroy the window with DestroyWindow as response tp WM_CLOSE notification, or just let DefWindowProc do the job for you.

Cheers,

-chris

jj2007

Quote from: gwapo on February 03, 2009, 05:35:52 AM
So there, hopefully, that's more helpful than my previous hasty comment.

Extremely helpful, thanks Chris :U