Announcement

Collapse
No announcement yet.

What's the proper point to SaveWindowPosition?

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • What's the proper point to SaveWindowPosition?

    When the user shuts down MyApp, I want my SaveWindowPosition(hDlg) routine to store the proper values in MyApp.ini...

    There are two ways to exit MyApp:
    - the sysmenu's X button
    - my own Quit command button

    My Quit button is created as part of the main dialog, but has its own callback:

    Code:
    CallBack Function CB_Quit() As Long
       Local lResult As Long
    
       If Cb.Msg = %wm_command And CbCtlMsg = %bn_clicked Then
          If gConfirm Then
             lResult = MsgBox ("Are you sure?",%mb_yesno,"Quit?")
             If lResult = %idno Then Exit Function
          End If
    
          SaveWindowPosition(hDlg)  
    
          Dialog End Cb.Hndl, 0 ' Return 0
          Function = 1          ' no further processing...
       End If
    End Function
    SaveWindowPosition(hDlg) doesn't seem to be working here, and I'm presuming that by the time we arrive here, hDlg is no longer valid. Added: NOPE! It works fine, but is missed by exiting via the sysmenu X button.

    So I looked for info in the SDK about how to capture the X button from the sysmenu. Here's what I found:

    When the user chooses the Close command, Windows sends a WM_COMMAND message to the dialog box procedure with the wParam parameter set to IDCANCEL.
    I also searched lots of source code on the forum and found people also used the wm_destroy message.

    SO, I turned my attention to my main dialog's callback DlgProc(). It would appear that I have the following two choices:

    Code:
       Select Case CbMsg
       Case %wm_command
          If Cb.Ctl = %IDCANCEL Then
             SaveWindowPosition(hDlg)
          End If
    
       Case %wm_destroy
          SaveWindowPosition(hDlg)
    Added:
    By putting a msgbox in each point in code, I see that:

    - clicking the Quit button, the Quit CB gets processed first, then we go through the wm_command/CB.CTL=idcancel, and finally we go through wm_destroy

    - clicking on the sysmenu's X button, we go through the wm_command/CB.CTL=idcancel, and through wm_destroy

    and that the same hDlg value is available at all 3 points.



    QUESTION: Is there a good reason to use wm_command/CB.CTL=idcancel versus wm_destroy, (or NOT to)??


    Thanks!
    Last edited by John Montenigro; 21 Feb 2009, 11:57 AM. Reason: added info from further testing

  • #2
    I always do "ini file update" stuff like this on WM_DESTROY of primary window.

    When you get WM_DESTROY, it's a NOTIFICATION, not an OPPORTUNITY, so you KNOW that ain't nothin' else gonna happen with that window.... i.e, it really IS "the end."
    Michael Mattias
    Tal Systems (retired)
    Port Washington WI USA
    [email protected]
    http://www.talsystems.com

    Comment


    • #3
      I usually do that sort of thing at wm_syscommand and check for sc_close. That way I can ask the user if he really wants to quit. If so I can also send messages to other dialogs/processes that may be active and write to any files that may need updating before actually quiting.
      Walt Decker

      Comment


      • #4
        OK, two different reponders, two different responses. I understand the reasons given, but is one way more preferred? and WHY?

        Or, maybe I'm asking the wrong question... OK how about this:

        What are the DISadvantages to doing the final tasks at each of the two suggested points?

        Comment


        • #5
          WM_DESTROY is the best place.

          The windows in the application all still exist during WM_DESTROY so it is a good place to get their positions and determine which ones are maximized or minimized.

          When Windows fires the WM_NCDESTROY message to the application then you know that it is really the end... the non-client area of the remaining window in the application is being destroyed. All other windows in the application have already been destroyed at that point.
          Paul Squires
          FireFly Visual Designer (for PowerBASIC Windows 10+)
          Version 3 now available.
          http://www.planetsquires.com

          Comment


          • #6
            Hi John,

            If you're just collecting data before closing the application I think, as Paul suggested, WM_DESTROY is the best place. However, if you want to give the user the option to abort closing the application as suggested by Walt, then WM_DESTROY may be too late. You'd have heard the mine's click by then, the best you could do is get your thoughts in order!

            Or, is it still possible to abort from a WM_DESTROY message?
            Regards,
            Marc

            Comment


            • #7
              I've never been able to abort from a wm_destroy message. Further, I've had cases where the app is performing some task, e.g., a file search for keys or key words when the user decides to quite the search. All the dialogs end but, because the app is still performing a task it keeps running. Therefore, I prefer wm_syscommand because it gives me the opperatunity to end everything and the user the chance to change his mind; besides which I'm getting a little senile and I occasional hit the quit button unintentionally.
              Walt Decker

              Comment


              • #8
                Originally posted by Walt Decker View Post
                ...besides which I'm getting a little senile and I occasional hit the quit button unintentionally.
                Now, there's something I can identify with Thanks for the explanation.
                Regards,
                Marc

                Comment


                • #9
                  if you want to give the user the option to abort closing the application as suggested by Walt, then WM_DESTROY may be too late.
                  Not "may be" too late, it IS too late!

                  BTW, let's make sure we are on the same page here... WM_DESTROY does not mean "program is ending", it means "the window which was sent this notification is being destroyed."

                  And PostQuitMessage does not end the program, either: it posts a WM_QUIT message to the message queue of the calling thread.

                  What happens after these things depends on your program code.

                  Eg, here is an example of posting a quit message which does NOT "end the program":

                  Progress Bar Dialog for PB/CC programs October 24, 2002
                  (Ignore the "PB/CC" in the thread title. This will work in a GUI program exactly the same way).

                  In this demo, PostQuitMessage simply causes the thread function to end.
                  Michael Mattias
                  Tal Systems (retired)
                  Port Washington WI USA
                  [email protected]
                  http://www.talsystems.com

                  Comment


                  • #10
                    Michael is correct.

                    An easy example: create 2 dialogs with parent as %hwnd_desktop (0). Close either window and the other continues to run unless, of course, you send wm_destroy to the other window.

                    The same applies if the app is in some process that has no idea that the user wishes to quit. The windows may be destroyed but the app continues to run until the process finishes and you may get a strange GPF depending on what the process is supposed to do.

                    Another place you might want to do "clean-up/user quit option" is in wm_close; however, I've had little luck with that as an "abort quit" location.
                    Walt Decker

                    Comment


                    • #11
                      >wm_close; however, I've had little luck with that as an "abort quit" location.

                      Show failing code, that's a real good place to do this and it should be working nicely.

                      What I do is anyplace a user can 'abort' (eg, hit the exit button, or use the system menu or hit Alt-F4,) is intercept that message, post WM_CLOSE, and eat the initiating message. Then under WM_CLOSE (one place) I put all my "check if OK to exit" code. If it is OK to exit, you allow WM_CLOSE close to go to the default handler; if not, you eat that one, too.

                      Works out real nice for me and it should for you, too.

                      MCM
                      Michael Mattias
                      Tal Systems (retired)
                      Port Washington WI USA
                      [email protected]
                      http://www.talsystems.com

                      Comment


                      • #12
                        You say your code intercepts and eats that message. I'm understanding (probably incorrectly) that your code does something, and then signals the OS that the original message has been handled. Is that correct? If so, how do you create that signal?

                        Sorry to be a dullard, but a little code would go a long way to helping me visualize what you're describing.


                        I suppose I've never completely understood how messages are processed/handled. I've only used the callback routine to handle messages that I know my controls will need to process.

                        But this raises fundamental questions for me about the larger context. Your statement makes me wonder what's going on outside MyApp's callback.

                        Besides my callback, who else is seeing that message? What is the "lifespan" of a message? How long does a message "last"? At what point is it finished, and how do we know? Who finishes it and how?

                        I realize that these questions are beyond a simple message response, but if anyone can point me to reading material that I can study, I'd greatly appreciate it!

                        Thanks!

                        Comment


                        • #13
                          This is a one-screen maintenance, which does not allow the user to exit with 'bad data' on the screen except by hitting 'cancel.'

                          What this code does is intercept SC_CLOSE and "pretends" the user actually cliked OK, which edits, saves and exits.

                          Code:
                              CASE %WM_SYSCOMMAND
                                  ' route request to close thru ID OK button
                                  IF  (wParam AND &hFFF0)  = %SC_CLOSE THEN
                                        PostButtonClick  (hWnd, %IDOK)
                                        FUNCTION = 0
                                        EXIT FUNCTION
                                  END IF
                          Not too hard, huh?

                          You can do the same posting a WM_CLOSE , and in WM_CLOSE returning zero to bypass the default handling, which is to destroy the window. This may not work with DDT dialogs; IIRC DDT does not handle "meaningful return value of zero" all that well.

                          But there's an easy workaround for that.. just post a private message instead of WM_CLOSE; in that message processing only do a "DIALOG END" if conditions are right.

                          ie
                          Code:
                              CASE %WM_SYSCOMMAND
                                  IF  (wParam AND &hFFF0)  = %SC_CLOSE THEN
                                       DIALOG POST  %PWM_REQUEST_TO_END, %NULL, %NULL
                                       FUNCTION = 0
                                       EXIT FUNCTION
                                  END IF
                          
                           CASE %PWM_REQUEST_TO_END 
                               IF (OK to exit) THEN 
                                   DIALOG END CBHNDL, return_code 
                               END IF
                          At least I think that should work. (Not a 'DDT' guy).
                          Michael Mattias
                          Tal Systems (retired)
                          Port Washington WI USA
                          [email protected]
                          http://www.talsystems.com

                          Comment


                          • #14
                            OK, here's what I've learned and what's working:


                            Code:
                            CallBack Function CB_Quit() As Long
                               Local lResult As Long
                            
                               If Cb.Msg = %wm_command And CbCtlMsg = %bn_clicked Then
                                  If gConfirm Then    'yes, this could be a param instead of a global...
                                     lResult = MsgBox ("Are you sure?",%mb_yesno,"Quit?")
                                     If lResult = %idno Then Exit Function
                                  End If
                            
                            '      MsgBox "from CB_Quit (1)" & Str$(hDlg),,"Exit message"  'while testing
                                  SaveWindowPosition(hDlg)  
                            
                                  Dialog End Cb.Hndl, 0 
                                  Function = 1          
                               End If
                            End Function
                            
                            
                            
                            CallBack Function DlgProc() As Long
                              ...setup code...
                            
                              Select Case CbMsg
                               Case %WM_SYSCOMMAND
                                  If (CbWParam And &HFFF0) = %sc_close Then  'traps both ALT-F4 and the X button, AND destroys dialog
                                     SaveWindowPosition(hDlg)
                                     'you can save windows positions, files, wait till other threads finish, etc.
                                     ' but this is the road to inevitable and irreversible destruction (AFAICT)
                                  End If                     
                            
                            
                               Case %wm_command
                                  If Cb.WParam = %idcancel Then   'traps the Esc key...but does NOT destroy the dialog
                                     'you can do pretty much anything you want to, including a return to processing.
                                     'but if you DO want to end here, AND you don't want to duplicate the termination code
                                     'that you've built into your Quit button, then instead of direct shutdown (Dialog End Cb.Hndl,0)...
                                     Control Post hDlg, %CMD_Quit, %BM_CLICK, 0, 0  '...send us off to the Quit button's code
                                     'Control POST here is better than SEND, since we want to get into the Quit code and not come back.
                                  End If
                            
                            
                               Case %wm_destroy
                                     'leave it alone. there's nothing you can do here that you can't handle above...
                                     'at this point, the window is being destroyed. (not sure if hDlg is even valid anymore)
                            User can end via: Quit Button, system menu's X, ALT-F4, and depending on if I want to allow it, the Esc key.

                            My window positions are being saved properly and reliably, and I'm happy with the results!

                            Thanks for all the input!

                            Comment


                            • #15
                              ' but this is the road to inevitable and irreversible destruction (AFAICT)
                              No

                              Code:
                                CASE %WM_SYSCOMMAND  'check for file saving
                                  IF (CBWPARAM AND &HFFF0) = %SC_CLOSE THEN
                                      C = %MB_YESNO OR %MB_TASKMODAL OR %MB_DEFBUTTON1 OR _
                                            %MB_ICONQUESTION
                                      C = MSGBOX("Save first?",C, "SAVE FIRST?")
                              ' ===================================================
                              '   if you want to abort the close then return 1 otherwise return 0
                              '====================================================
                                      IF C = %IDYES THEN FUNCTION = 1 ELSE FUNCTION = 0
                                  END IF
                              From where is "If Cb.WParam = %idcancel" coming? Without seeing more code it is difficult to determine what is going on.
                              Walt Decker

                              Comment


                              • #16
                                I didn't know that returning 1 could abort the close. I'll have to go thru my shutdown scenarios again using it. Thanks for that info.

                                I was seeing %idcancel as a result of pressing the Esc key...

                                Comment


                                • #17
                                  >was seeing %idcancel as a result of pressing the Esc key...

                                  You saw correctly. Pressing <ESC> WILL get you a WM_COMMAND/IDCANCEL notification, just like pressing <ENTER> will get you WM_COMMAND/IDOK.
                                  Michael Mattias
                                  Tal Systems (retired)
                                  Port Washington WI USA
                                  [email protected]
                                  http://www.talsystems.com

                                  Comment


                                  • #18
                                    I was catching up on this thread. One of the posts had to do with using WM_CLOSE vs WM_SYSCOMMAND to abort closing an app. I think it was MCM who said WM_CLOSE should work.

                                    I can get WM_SYSCOMMAND code to abort, but not with WM_CLOSE

                                    This doesn't work:

                                    Code:
                                    #Compile Exe
                                    #Dim All
                                    
                                    Function PBMain () As Long
                                       Local hDlg As Dword
                                       Dialog New Pixels, 0, "Test",300,300,200,200, %WS_OverlappedWindow To hDlg
                                       Dialog Show Modal hdlg Call DlgProc
                                    End Function
                                    
                                    CallBack Function DlgProc() As Long
                                       Dim Style&
                                       Select Case Cb.Msg
                                            Case %WM_Close
                                                 Style& = %MB_OkCancel Or %MB_IconQuestion Or %MB_TaskModal
                                                 If MsgBox("Are you sure?", Style&, "Close Application?") = %IdOk Then
                                                    Function = 0    'False - go ahead and destroy
                                                 Else
                                                    Function = 1   'True - abort the close - no further processing needed
                                                 End If
                                       End Select
                                    End Function
                                    But this does:
                                    Code:
                                    #Compile Exe
                                    #Dim All
                                    #Include "win32api.inc"
                                    
                                    Function PBMain () As Long
                                       Local hDlg As Dword
                                       Dialog New Pixels, 0, "Test",300,300,200,200, %WS_OverlappedWindow To hDlg
                                       Dialog Show Modal hdlg Call DlgProc
                                    End Function
                                    
                                    CallBack Function DlgProc() As Long
                                       Dim Style&
                                       Select Case Cb.Msg
                                            Case %WM_SYSCOMMAND
                                                 If (Cb.WParam And &HFFF0) = %SC_Close Then          'trap Alt-F4 and X Button
                                                    Style& = %MB_OkCancel Or %MB_IconQuestion Or %MB_TaskModal
                                                    If MsgBox("Are you sure?", Style&, "Close Application?") = %IdOk Then
                                                       Function = 0    'False - go ahead and destroy
                                                    Else
                                                       Function = 1   'True - abort the close - no further processing needed
                                                    End If
                                                 End If
                                       End Select
                                    End Function
                                    MSDN highlights WM_CLOSE as the place to abort:

                                    An application can prompt the user for confirmation, prior to destroying a window,
                                    So I assume I'm doing something wrong.

                                    Comment


                                    • #19
                                      If you're trapping the WM_CLOSE after the SC_CLOSE has been processed, that is, you clicked the X in the caption bar, too late, application is already in shutdown mode.

                                      AFAIK anyway.
                                      Furcadia, an interesting online MMORPG in which you can create and program your own content.

                                      Comment


                                      • #20
                                        Colin,

                                        That would make sense, but then it leaves the MSDN quote about wm_close still a mystery.

                                        If I have to process sc_close in wm_syscommand first, what's the point of using wm_close?

                                        Comment

                                        Working...
                                        X