Announcement

Collapse
No announcement yet.

WM_Close Not Received After DestroyWindow

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

  • WM_Close Not Received After DestroyWindow

    Reading MSDN, I don't find anything that explicitly says that WM_Close will not be received when WM_Destroy is first received (by calling DestroyWindow). But, that's what seems to happen.

    In the Dialog Box code below, if I close with an X, then WM_Close is received. But if I close with a right mouse click, then WM_Close is not recived.

    Is that how it is supposed to work?

    Code:
    'SDK Dialog compilable example:
    #Compile Exe
    #Dim All
    #Include "win32api.inc"
    
    %IDM_Exit = 500
    
    Function PBMain () As Long
       Local template As DLGTEMPLATE
       Local hInst As Dword
       Local hParent As Dword
       Local hAccel As Dword
       Local hDlg As Dword
       Local msg As tagMSG
    
       hInst = GetModuleHandle( ByVal %NULL )
       template.style = %WS_ThickFrame Or %WS_Caption Or %WS_SysMenu Or %WS_MaximizeBox Or %WS_MinimizeBox Or %WS_Overlapped Or %WS_SizeBox Or %WS_Visible
       template.dwExtendedStyle = %WS_Ex_AppWindow Or %WS_Ex_OverlappedWindow
       template.cdit = 0
       template.x = 50
       template.y = 50
       template.cx = 320
       template.cy = 200
    
       CreateDialogIndirect hInst, template, hParent, CodePtr( DlgProc ) To hDlg   'modeless dialog box
       ShowWindow hDlg, %SW_Show
       UpdateWindow hDlg
    
       While GetMessage(Msg, %NULL, 0, 0) > 0
          If IsFalse TranslateAccelerator (hDlg, hAccel, Msg) Then
             If IsFalse ISDialogMessage (hDlg, Msg) Then
                TranslateMessage Msg
                DispatchMessage  Msg
             End If
          End If
       Wend
    
    End Function
    
    Function DlgProc (ByVal hWnd As Dword, ByVal wMsg As Dword, ByVal wParam As Dword, ByVal lParam As Long) Export As Long
       Select Case wMsg
          Case %WM_ContextMenu
             DestroyWindow hWnd
          Case %WM_Destroy
             ? "destroy"
             PostQuitMessage(0)
          Case %WM_Close
             ? "close"
             PostQuitMessage(0)
       End Select
    End Function
    Although, this from MSDN on WM_Close ...
    An application can prompt the user for confirmation, prior to destroying a window, by processing the WM_CLOSE message and calling the DestroyWindow function only if the user confirms the choice.
    sort of implies that WM_Close will not be called following DestroyWindow - but doesn't quite say that.

    But in that case, where WM_Close would likely call the PostQuitMessage, why would DestroyWindow be needed? I thought when the message loop was closed, that Windows destroys all windows in the process, so why the need for DestroyWindow at that point?

  • #2
    DestroyWindow posts a WM_DESTROY message, not WM_CLOSE. Therefore, the best place to use PostQuitMessage is in WM_DESTROY.
    Forum: http://www.jose.it-berater.org/smfforum/index.php

    Comment


    • #3
      Hi Jose,
      Thanks for the response.

      So, it would seem that the location of PostQuitMessage would need to fit the way(s) in which the user may close the app.

      With DestroyWindow, WM_Destroy is received but not WM_Close.

      But by just closing a Dialog Box with the X, then WM_Destroy is not received and PostQuitMessage would need to be placed in the WM_Close, as in the example below where WM_Close is received but not WM_Destroy.

      That's unlike a DDT dialog, where using the X does result in a WM_Destroy message.

      Code:
      #Compile Exe
      #Dim All
       #Include "win32api.inc"
       %IS_MODAL = 1
       Function PBMain () As Long
        Local template As DLGTEMPLATE
        Local hInst As Dword
        Local hParent As Dword
        Local hDlg As Dword
        Local x As WStringZ Ptr
         hInst = GetModuleHandle( ByVal %NULL )
         template.style = %WS_ThickFrame Or %WS_Caption Or %WS_SysMenu Or %WS_MaximizeBox Or %WS_MinimizeBox Or %WS_Overlapped Or %WS_SizeBox Or %WS_Visible
        template.dwExtendedStyle = %WS_Ex_AppWindow Or %WS_Ex_OverlappedWindow
        template.cdit = 0
        template.x = 50
        template.y = 50
        template.cx = 320
        template.cy = 200
       #If %Def( %IS_MODAL )
        ' Create modal dialog box
        DialogBoxIndirect hInst, template, hParent, CodePtr( DlgProc ) To hDlg
      #Else
        ' Create modaless dialog box
        CreateDialogIndirect hInst, template, hParent, CodePtr( DlgProc ) To hDlg
        ShowWindow hDlg, %SW_Show
        UpdateWindow hDlg
         Local msg As MSG_type
         While GetMessage( msg, hDlg, 0, 0 )>0
          If Not IsDialogMessage( hDlg, msg ) Then
              TranslateMessage msg
              DispatchMessage msg
          End If
        Wend
      #EndIf
      End Function
       Function DlgProc( ByVal hDlg As Dword, ByVal hMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
        Select Case hMsg
          Case %WM_Close
            PostQuitMessage(0)
        End Select
      End Function

      Comment


      • #4
        Use:

        Code:
            Case %WM_Close
              DestroyWindow hDlg
            Case %WM_DESTROY
              PostQuitMessage(0)
        Forum: http://www.jose.it-berater.org/smfforum/index.php

        Comment


        • #5
          When clicking the X button, Windows DOES NOT destroy the dialog, but sends an WM_CLOSE message and leaves you to decide what to do, i.e. ignore it or destroy the dialog with DestroyWindow, and after calling DestroyWindow, WM_DESTROY will be received. Don't send PostQuitMessage in WM_CLOSE because, at this point, the dialog has not been destroyed.
          Forum: http://www.jose.it-berater.org/smfforum/index.php

          Comment


          • #6
            Also worth pointing out that PostQuitMessage is only needed if you really intend to exit the application when that particular window closes.

            In other tools you might have noticed a choice to exit on a "main" window closing or on the last window closing. If you always PostQuitMessage in WM_DESTROY you're essentially saying, exit if any window closes.

            Here's an example of exiting on only Main closing or on last window closing.


            Code:
            #Compile Exe
            #Dim All
             
            #Include "win32api.inc"
             
            '%CLOSE_ON_MAIN = 1
             
            Global hMain, hDlg As Dword
            Global activeWindows As Long
             
            Function PBMain () As Long
              MakeDialog "Main", 50 To hMain
              MakeDialog "Second", 390 To hDlg
              SetFocus hMain
             
              Local msg As MSG_type
              While GetMessage( msg, %NULL, 0, 0 )>0
                If Not IsDialogMessage( %NULL, msg ) Then
                  TranslateMessage msg
                  DispatchMessage msg
                End If
              Wend
            End Function
             
            Function DlgProc( ByVal hDlg As Dword, ByVal hMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
              Select Case hMsg
            #If Not %Def( %CLOSE_ON_MAIN )
                Case %WM_InitDialog
                  activeWindows += 1
            #EndIf
             
                Case %WM_Close
                  DestroyWindow hDlg
             
                Case %WM_Destroy
            #If %Def( %CLOSE_ON_MAIN )
                  If hDlg = hMain Then
                    PostQuitMessage(0)
                  End If
            #Else
                  activeWindows -= 1
                  If activeWindows=0 Then
                    PostQuitMessage(0)
                  End If
            #EndIf
              End Select
            End Function
             
            Function MakeDialog( title As String, x As Long ) As Dword
              Local hInst, hDlg As Dword
              Local template As DLGTEMPLATE
             
              hInst = GetModuleHandle( ByVal %NULL )
             
              template.style = %WS_ThickFrame Or %WS_Caption Or %WS_SysMenu Or %WS_MaximizeBox Or %WS_MinimizeBox Or %WS_Overlapped Or %WS_SizeBox Or %WS_Visible
              template.dwExtendedStyle = %WS_Ex_AppWindow Or %WS_Ex_OverlappedWindow
              template.cdit = 0
              template.x = x
              template.y = 50
              template.cx = 320
              template.cy = 200
             
              ' Create modaless dialog box
              CreateDialogIndirect hInst, template, 0, CodePtr( DlgProc ) To hDlg
             
              SetWindowText hDlg, ByVal StrPtr(title)
             
              ShowWindow hDlg, %SW_Show
              UpdateWindow hDlg
             
              Function = hDlg
            End Function
            If %CLOSE_ON_MAIN is commented out, the app exits when the last window closes.

            If %CLOSE_ON_MAIN is uncommented, the app exits only when main closes.
            LarryC
            Website
            Sometimes life's a dream, sometimes it's a scream

            Comment


            • #7
              Guys,
              Thanks for the clarifications, and code!

              While experience in DDT makes most of the SDK stuff seemingly familiar, there are nuances all over the place that one has to get familiar with!

              Comment


              • #8
                Some time ago there was a post here (it was by James Fuller) about this WM_CLOSE/WM_DESTROY thing and I made the comment that for a seemingly simple thing its surprisingly complicated. And MCM fired back something to the effect that it indeed isn't very complicated. I stand by my original comment. There are a lot of subtle nuances to it. And make the slightest miscalculation with it and you'll find out about it pretty quick.

                Generally, I'd say that if you don't intend to throw up a dialog questioning the user as to whether he/she indeed does want to exit, then its best to just forget WM_CLOSE and just do WM_DESTROY code.
                Fred
                "fharris"+Chr$(64)+"evenlink"+Chr$(46)+"com"

                Comment


                • #9
                  I love playing with this stuff! Here's an example whose only functionality and use is the output.txt file it creates to log messages. It just creates a blank main form that you can x out of as soon as it starts. But I put file output statements in it starting from WinMain() and ending in WM_CLOSE and WM_DESTROY that clearly show the sequencing and nesting of function calls. Here's the output.txt file first...

                  Code:
                  Entering WinMain()
                    hWnd =  0   <<< Before CreateWindowEx()
                    Entering fnWndProc_OnCreate()
                      Wea.hWnd =  983498 
                    Leaving fnWndProc_OnCreate()
                    hWnd =  983498   <<< After CreateWindowEx()
                    Entering fnWndProc_OnClose()
                      Wea.hWnd =  983498 
                      Entering fnWndProc_OnDestroy()
                        Wea.hWnd =  983498 
                      Leaving fnWndProc_OnDestroy()
                    Leaving fnWndProc_OnClose()
                  Leaving WinMain()
                  And here's the program ...

                  Code:
                  #Compile Exe
                  %UNICODE            = 1
                  %Debug              = 1
                  #If %Def(%UNICODE)
                      Macro ZStr      = WStringz
                      Macro BStr      = WString
                  #Else
                      Macro ZStr      = Asciiz
                      Macro BStr      = String
                  #EndIf
                  #Include "Windows.inc"
                  
                  Type WndEventArgs
                    wParam As Long
                    lParam As Long
                    hWnd   As Dword
                    hInst  As Dword
                  End Type
                  
                  Declare Function FnPtr(wea As WndEventArgs) As Long
                  
                  Type MessageHandler
                    wMessage As Long
                    dwFnPtr As Dword
                  End Type
                  
                  #if %Def(%Debug)
                  Global fp As Integer
                  #endif
                  
                  
                  Function fnWndProc_OnCreate(wea As WndEventArgs) As Long
                    Local pCreateStruct As CREATESTRUCT Ptr
                  
                    #If %Def(%Debug)
                    Print #fp, "  Entering fnWndProc_OnCreate()"
                    Print #fp, "    Wea.hWnd = " Wea.hWnd
                    #EndIf
                    pCreateStruct=wea.lParam
                    [email protected]
                    #If %Def(%Debug)
                    Print #fp, "  Leaving fnWndProc_OnCreate()"
                    #EndIf
                  
                    fnWndProc_OnCreate=0
                  End Function
                  
                  
                  Function fnWndProc_OnClose(wea As WndEventArgs) As Long
                    #If %Def(%Debug)
                    Print #fp, "  Entering fnWndProc_OnClose()"
                    Print #fp, "    Wea.hWnd = " Wea.hWnd
                    #EndIf
                    SendMessage(Wea.hWnd,%WM_DESTROY,0,0)
                    #If %Def(%Debug)
                    Print #fp, "  Leaving fnWndProc_OnClose()"
                    #EndIf
                  
                    fnWndProc_OnClose=0
                  End Function
                  
                  
                  Function fnWndProc_OnDestroy(wea As WndEventArgs) As Long
                    #If %Def(%Debug)
                    Print #fp, "    Entering fnWndProc_OnDestroy()"
                    Print #fp, "      Wea.hWnd = " Wea.hWnd
                    #EndIf
                    Call PostQuitMessage(0)
                    #If %Def(%Debug)
                    Print #fp, "    Leaving fnWndProc_OnDestroy()"
                    #EndIf
                    
                    fnWndProc_OnDestroy=0
                  End Function
                  
                  
                  Sub AttachMessageHandlers()
                    Dim MsgHdlr(2) As Global MessageHandler 'Associate Windows Message With Message Handlers
                    MsgHdlr(0).wMessage=%WM_CREATE          : MsgHdlr(0).dwFnPtr=CodePtr(fnWndProc_OnCreate)
                    MsgHdlr(1).wMessage=%WM_CLOSE           : MsgHdlr(1).dwFnPtr=CodePtr(fnWndProc_OnClose)
                    MsgHdlr(2).wMessage=%WM_DESTROY         : MsgHdlr(2).dwFnPtr=CodePtr(fnWndProc_OnDestroy)
                  End Sub
                  
                  
                  Function fnWndProc(ByVal hWnd As Long,ByVal wMsg As Long,ByVal wParam As Long,ByVal lParam As Long) As Long
                    Local wea As WndEventArgs
                    Register iReturn As Long
                    Register i As Long
                  
                    For i=0 To 2
                      If wMsg=MsgHdlr(i).wMessage Then
                         wea.hWnd=hWnd: wea.wParam=wParam: wea.lParam=lParam
                         Call Dword MsgHdlr(i).dwFnPtr Using FnPtr(wea) To iReturn
                         fnWndProc=iReturn
                         Exit Function
                      End If
                    Next i
                  
                    fnWndProc=DefWindowProc(hWnd,wMsg,wParam,lParam)
                  End Function
                  
                  
                  Function WinMain(ByVal hIns As Long,ByVal hPrev As Long,ByVal lpCL As Asciiz Ptr,ByVal iShow As Long) As Long
                    Local szAppName As ZStr*16
                    Local wc As WndClassEx
                    Local hWnd As Dword
                    Local Msg As tagMsg
                  
                    #if %Def(%Debug)
                    fp=Freefile : Open "Output.txt" For Output As #fp
                    Print #fp, "Entering WinMain()"
                    #endIf
                    szAppName="Form1"                               : Call AttachMessageHandlers()
                    wc.lpszClassName=VarPtr(szAppName)              : wc.lpfnWndProc=CodePtr(fnWndProc)
                    wc.cbClsExtra=0                                 : wc.cbWndExtra=0
                    wc.style=%CS_HREDRAW Or %CS_VREDRAW             : wc.hInstance=hIns
                    wc.cbSize=SizeOf(wc)                            : wc.hIcon=LoadIcon(%NULL, ByVal %IDI_APPLICATION)
                    wc.hCursor=LoadCursor(%NULL, ByVal %IDC_ARROW)  : wc.hbrBackground=%COLOR_BTNFACE+1
                    wc.lpszMenuName=%NULL
                    Call RegisterClassEx(wc)
                    #If %Def(%Debug)
                    Print #fp, "  hWnd = " hWnd "  <<< Before CreateWindowEx()"
                    #EndIf
                    hWnd=CreateWindowEx(0,szAppName,"Form1",%WS_OVERLAPPEDWINDOW,200,100,325,300,0,0,hIns,ByVal 0)
                    #If %Def(%Debug)
                    Print #fp, "  hWnd = " hWnd "  <<< After CreateWindowEx()"
                    #EndIf
                    Call ShowWindow(hWnd,iShow)
                    While GetMessage(Msg,%NULL,0,0)
                      TranslateMessage Msg
                      DispatchMessage Msg
                    Wend
                    #If %Def(%Debug)
                    Print #fp, "Leaving WinMain()"
                    Close #fp
                    #EndIf
                  
                    Function=msg.wParam
                  End Function
                  
                  #if 0
                  Entering WinMain()
                    hWnd =  0   <<< Before CreateWindowEx()
                    Entering fnWndProc_OnCreate()
                      Wea.hWnd =  983498 
                    Leaving fnWndProc_OnCreate()
                    hWnd =  983498   <<< After CreateWindowEx()
                    Entering fnWndProc_OnClose()
                      Wea.hWnd =  983498 
                      Entering fnWndProc_OnDestroy()
                        Wea.hWnd =  983498 
                      Leaving fnWndProc_OnDestroy()
                    Leaving fnWndProc_OnClose()
                  Leaving WinMain()
                  #EndIf
                  The WM_CLOSE and WM_DESTROY code is a bit noteworthy. Note that in processing WM_DESTROY we're nested three deep in function calls. At the first level, we're still in WinMain() because the message loop is still running there and that continues for the life of the app. At the second level we're in WM_CLOSE because that's what gets called (if it exists - otherwise DefWindowProc() processes it) when the x button is clicked. But while in WM_CLOSE processing I have a SendMessage(WM_DESTROY) call and that puts us yet another level deeper into the recursion. But it all works itself out in the end and all is 'happily everafter'.
                  Fred
                  "fharris"+Chr$(64)+"evenlink"+Chr$(46)+"com"

                  Comment


                  • #10
                    Also worth pointing out that PostQuitMessage is only needed if you really intend to exit the application when that particular window closes.
                    WM_CLOSE does not exit the application.

                    PostQuitMessage() does not exit the application.

                    Your application ends when WinMain runs out of code to execute and the compiler-inserted code calls ExitProcess() or you explicitly call ExitProcess() from your code.

                    PostQuitMessage does not exit application:
                    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).

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

                    Comment


                    • #11
                      While experience in DDT makes most of the SDK stuff seemingly familiar, there are nuances all over the place that one has to get familiar with!
                      Learn SDK FIRST. It will make it easier to understand exactly what DDT is doing.

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

                      Comment

                      Working...
                      X