Solved

MFC Application is not closing completely

Posted on 2009-07-09
26
1,723 Views
Last Modified: 2013-11-20
I have inherited an application that under repeatable circumstances the program will not close completely when closing with the "red X".  All windows close, but the application is still running.  When paused in debug mode, it appears that the main thread is continuing to process the message loop, although the PeekMessage() function that the loop calls consistently returns a NULL message.

The same circumstances were previously causing an exception due to an attempted access of deallocated structures.  After modifying the code to prevent those accesses, the current situation appeared.

To give a basic idea of what is happening in the code under these circumstances:
-The application uses the MDI structure and the issues appears when closing the application with one of the views open.
-Upon initialization of this view, a timer is set to communicate with a device periodically.
- When the user closes the program, the class calls a function to shut down the timer and the communication port.  All of this seems to complete successfully, except for the fact that the application is still running.

Thank you for any help you may offer.
0
Comment
Question by:psft
  • 11
  • 10
  • 5
26 Comments
 
LVL 44

Assisted Solution

by:AndyAinscow
AndyAinscow earned 100 total points
Comment Utility
This sounds like something is still open and in use.

>>When the user closes the program, the class calls a function to shut down the timer and the communication port.

Have you checked that it really does do that?  (If the function does not return then that could be your problem)
0
 
LVL 1

Author Comment

by:psft
Comment Utility
>>Have you checked that it really does do that?  (If the function does not return then that could be your problem)

I have verified that the function does return based on a TRACE() I added.
0
 
LVL 44

Expert Comment

by:AndyAinscow
Comment Utility
Without having the code this is not so easy to track down.

Can you put a breakpoint then step into code - see if something else later is hanging
0
 
LVL 1

Author Comment

by:psft
Comment Utility
I have attempted to do so and have found that it hangs on a 'call' line in assembly.  I don't know how to analyze that data to get useful information.  The call stack seems to be unhelpful as it shows the message loop calling the CloseDocument function  which calls a bunch of USER32 and NTDLL functions.
0
 
LVL 39

Assisted Solution

by:itsmeandnobodyelse
itsmeandnobodyelse earned 150 total points
Comment Utility
>>>> have found that it hangs on a 'call' line in assembly.

There are two common reasons for that you experienced.

(1) some destructor of static class objects is running an infinite loop.

(2) the debugger has problems to evaluate/output the memory leak debug report.

The (2) means that your memory is corrupt at exit. You should set a breakpoint to the ExitInstance (either your overload or CWinApp:ExitInstance) and debug step into until you know more.

The (1) you could try to find out by setting breakpoints to each destructor of static class members.
0
 
LVL 1

Author Comment

by:psft
Comment Utility
I have checked the few static class members  for issues using the debugger, but they have all checked out OK.

I have also set a breakpoint at ExitInstance, but it seems to hang in a loop prior to that function being called.  Stepping through from one of the destructors shows that it hangs at a variety of points in assembly.
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
An MFC app (SDI or MDI) runs the message loop in CWinThread::Run. CWinThread is baseclass of CWinApp what is the baseclass of your app class. At end of YourApp::InitInstance it calls Run() and that is what the thing keeps on running. When the red x was clicked in the current frame it causes the menu handler of that frame to get a WM_COMMAND or WM_SYSCOMMAND with SC_CLOSE as second information. That generates the WM_CLOSE messages to the mainframe window or in a MDI to the current active MDIChildWnd what is a CFrameWnd either which you were handling in CMainframe::OnClose() or in YourMDIChildWnd::OnClose. At end of that member function you should have a call

      CFrameWnd::OnClose();

which must run as it has the task to close all views and docs associated to their frames and finally send a WM_QUIT to the message loop what breaks the infinite loop in CWinThread::Run and calls ExitInstance what is a virtual function, so it comes to YourApp::ExitInstance if you made an overload.

If it hangs before calling the ExitInstance it could be that any of the handlers called between wasn't calling its direct baseclass handler function well so that the WM_QUIT messages wasn't created.


To verify that what I told, set break-points to the OnClose functions of your frame classes. Check that they were called and that the CFrameWnd::OnClose was called as well. At this momemt you could set a breakpoint to the message loop in CWinThread::Run cause there will not be much messages if coming so far. Try to find out why the WM_QUIT wasn't send (search for WM_QUIT in all MFC sources, there are not so much occurrences and set break-points at the begin of these functions).

I know it is a bad job but once every MFC programmer must go thru ;-)
0
 
LVL 44

Assisted Solution

by:AndyAinscow
AndyAinscow earned 100 total points
Comment Utility
A comment of mine seems to have gone missing.


-Upon initialization of this view, a timer is set to communicate with a device periodically.
- When the user closes the program, the class calls a function to shut down the timer and the communication port.



Would it be possible to comment out both of the above?  Does it still exhibit the faulty behaviour?

What happens if you don't perform the shutdown of the comm port - does the app fail to stop every time now?
0
 
LVL 1

Author Comment

by:psft
Comment Utility
The CMainframe class in this application doesn't override the OnClose function.  I'm not sure how the CChildWnd gets linked in as I can't find the code that does it, but I suspect that happens behind the scenes when the Document Template is opened.

What seems to be occurring is that the OnDestroy function of the Document Template is being called.  There doesn't seem to be an WM_CLOSE message for the Document Template.  I put a TRACE in the OnClose of the CChildFrame and that never gets called, however, the Destructor is called.  (I'm not sure how the childframe gets linked in with the mainframe and doc template, but that is a different topic.)

How does WM_DESTROY get called and how is that different from WM_CLOSE?
0
 
LVL 1

Author Comment

by:psft
Comment Utility
I tested with no shutdown procedure for the timer and comm port and the application still hung on exit.
0
 
LVL 39

Assisted Solution

by:itsmeandnobodyelse
itsmeandnobodyelse earned 150 total points
Comment Utility
>>>> The CMainframe class in this application doesn't override the OnClose function
That would be ok, then the CMDIFrameWnd::OnClose would be called instead.

>>>> I am not sure how the CChildWnd gets linked in as
The CChildWnd is the wizard-generated frame class for the frame windows of the multiple documents in the MDI. In case you opened a new or existing document the associated frame takes the place of the main frame and menus and tool-buttons can change. So, when clicking on the red-x in the frame the message either goes to the mainframe (or its baseclass if not handled) in case you didn't decide for a specific document so far and the mainframe is still active or in case you already are working on a document and have (a) view(s) on it, the message goes to CChildWnd::OnClose  (or to the CMDIChildWnd if not handled).  

You may look at the IMPLEMENT_DYNCREATE macro in mainfrm.cpp and childwnd.cpp. With that macro the message hierarchy was determined. In order to work properly the message flow must have not be changed, i. e. the WM_CLOSE message *MUST* be handled by either of the frame window classes, derived classes or baseclasses.

>>>> How does WM_DESTROY get called and how is that different from WM_CLOSE?

The WM_DESTROY was a message directed to a window in order to destroy that window. That message was created by the framework for every window which needs to be destroyed for instance as a consequence of a previouse WM_CLOSE directed to the parent window (e. g. view or dialog) or grand parent window (e. g. frame window). You hardly will send WM_DESTROY yourself and you hardly will handle it. If you need special code after a window was destroyed you better handle the WM_NCDESTROY message which is the very last message to an already destroyed window. In that handler you even could have a 'delete this;', e. g. for modeless dialogs.

>>>> and the application still hung on exit.
As told: first spot the OnClose method which was called, then set a breakpoint to the message loop to find out why the WM_QUIT wasn't processed or sent.

Note, if neither OnClose was called you probably found the reason for hanging. The CFrameWnd::OnClose (CFrameWnd is the baseclass for both CMDICildWnd and CMDIFrameWnd).
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
>>>> There doesn't seem to be an WM_CLOSE message for the Document Template.
The template is only some kind of brace for frame, doument and view. As CMultiDocTemplate has CCmdTarget as a baseclass, the template can handle messages directed to it, but I doubt that it is a WM_DESTROY message as a template isn't a window (same as document and application)

>>>> I put a TRACE in the OnClose of the CChildFrame and that never gets called
Better set a breakpoint. TRACE statements are less reliable. In case of a heap corruption trace statements may be corrupted either. As told the CChildFrame::OnClose only was called if you already opened a document where the template associated uses the CChildWnd class as frame window. You may check InitInstance  of your application class how the CMDIDocTemplate was used and what frameclass was associated. See below code of one of my MDI projects which is extracted from CFoodApp::InitInstance
	// Register the application's document templates.  Document templates

	//  serve as the connection between documents, frame windows and views.

 	m_pSysTemplate = new CMultiDocTemplate(									IDR_SYSTEM,											RUNTIME_CLASS(CSystemDoc),										RUNTIME_CLASS(CFoodChildFrame),        // standard MDI child frame

		RUNTIME_CLASS(CSysSelView));

	AddDocTemplate(m_pSysTemplate);
 

 	m_pMasterTemplate = new CMultiDocTemplate(

		IDR_MASTER,

		RUNTIME_CLASS(CMasterDoc),

		RUNTIME_CLASS(CFoodChildFrame),        // standard MDI child frame

		RUNTIME_CLASS(CMasterSelView));

	AddDocTemplate(m_pMasterTemplate);
 

 	m_pComponentTemplate = new CMultiDocTemplate(

		IDR_COMPONENT_TREE,

		RUNTIME_CLASS(CComponentTreeDoc),

		RUNTIME_CLASS(CFoodChildFrame),        // standard MDI child frame

		RUNTIME_CLASS(CComponentTreeView));

	AddDocTemplate(m_pComponentTemplate);

																							

	m_pElementTemplate = new CMultiDocTemplate(

		IDR_ELEMENT_TREE,

		RUNTIME_CLASS(CElementTreeDoc),

		RUNTIME_CLASS(CFoodChildFrame),        // standard MDI child frame

		RUNTIME_CLASS(CElementTreeView));

	AddDocTemplate(m_pElementTemplate);

 

 	m_pMaterialTemplate = new CMultiDocTemplate(

		IDR_MATERIAL_TREE,

		RUNTIME_CLASS(CMaterialTreeDoc),

		RUNTIME_CLASS(CFoodChildFrame),        // standard MDI child frame

		RUNTIME_CLASS(CMaterialTreeView));

	AddDocTemplate(m_pMaterialTemplate);

 

 	m_pRecipeTemplate = new CMultiDocTemplate(

		IDR_RECIPE_TREE,

		RUNTIME_CLASS(CRecipeTreeDoc),

		RUNTIME_CLASS(CFoodChildFrame),        // standard MDI child frame

		RUNTIME_CLASS(CRecipeTreeView));

	AddDocTemplate(m_pRecipeTemplate);

 

Open in new window

0
 
LVL 1

Author Comment

by:psft
Comment Utility
That all makes sense, and I found where the Document Template gets added.

I did add a breakpoint to the OnClose function of the child frame class.  It doesn't seem to be getting called.  From what I can tell, clicking the "red X" causes a WM_CLOSE message to be sent to the MainFrame.  Should the CMDIFrameWnd::OnClose() function send a WM_CLOSE message to the child frame?  How does that process work?

Below is the code from the two classes for the IMPLEMENT_DYNxxxx.  There was no IMPLEMENT_DYNCREATE for CMainFrame, but it did have the IMPLEMENT_DYNAMIC although I don't know what their purposes are and how they are different.
IMPLEMENT_DYNAMIC(CMainFrame, CGoMDIFrameWnd)
 

IMPLEMENT_DYNCREATE(CChildFrame, CMDIChildWnd)

Open in new window

0
What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

 
LVL 44

Expert Comment

by:AndyAinscow
Comment Utility
>>I tested with no shutdown procedure for the timer and comm port and the application still hung on exit.

Is it possible to not start up the comm port side of things ?

0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
>>>> Should the CMDIFrameWnd::OnClose() function send a WM_CLOSE message to the child frame?  How does that process work?

Not necessarily. It depends on whether the child frame has an own system menu what handles the close, minimize, restore, ... . If  I remember rightly, the default is that the mainframe still handles the system menu (and the visualizing of tool-buttons and status fields) and that only the private menu was handled by the child frame if a different identifier was passed as first document to the CMultiDocTemplate.

>>>> There was no IMPLEMENT_DYNCREATE for CMainFrame, but it did have the IMPLEMENT_DYNAMIC although I don't know what their purposes are and how they are different.

Yes, my mainframe class has also *only* IMPLEMENT_DYNAMIC, what means that they support dynamic message maps. IMPLEMENT_DYNCREATE additional supports the capability for automatic instantiation of a derived class what is needed when the frames were created via the doc templates.

>>>> CGoMDIFrameWnd

What is that? From which calss was it derived from? Could you check whether the CGoMDIFrameWnd::OnClose was called at end of CMainFrame::OnClose and if there was an implementation of the CGoMDIFrameWnd::OnClose?

If possible post any OnClose function of any of your frame classes.
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
>>>> Should the CMDIFrameWnd::OnClose() function send a WM_CLOSE message to the child frame?
Most likely not. You would have recognized it if the the CChildWnd::OnClose would have been invoked.

The CMDIFrameWnd has access to all frames of an MDI and probably makes direct calls to these objects which would destroy them.
0
 
LVL 1

Author Comment

by:psft
Comment Utility
CGoMDIFrameWnd is derived from CMDIFrameWnd, but CMainFrame::OnClose() calls CMDIFrameWnd::OnClose(), not CGoMDIFrameWnd::OnClose().

Since CMainFrame is the active frame in this view, is there a way for me to call my exit routine from CMainFrame.  I don't see where CMainFrame has a pointer or object that provides access to to the subFrame, or view.
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
>>>> calls CMDIFrameWnd::OnClose(), not CGoMDIFrameWnd::OnClose().

That doesn't matter as long as there is no CGoMDIFrameWnd::OnClose().

>>>> is there a way for me to call my exit routine from CMainFrame

The CMainFrame could be retrieved from everywhere by AfxGetMainWnd().

Your application object could be retrieved from everywhere by AfxGetApp().

From the application you normally would have members for each CMultiDocTemplate.

From template you could reach document, views and their frame window like in the code below (note, the GetObject and objectID is my add-on).

But, the framework knows all these relationships. If it hangs like in your case, it normally won't help if you call any exit function before time as it should be called from the framework anyhow.

You could try to calling YourApp::OnFileExit from CMainFrame::OnClose if the OnFileExit invoked by menu File - Exit manages to stop the application while the "red X" does not. But you would need to add code so that there is no infinite loop when the OnFileExit somehow would call CMainFrame::OnClose in the following:

void CMainFrame::OnClose()
{
      static bool appexitCalled = false;
      if (appexitCalled == false)
      {
            appExitCalled = true;
            MyApp* pApp = (MyApp*)AfxGetApp();  // alternatively use theApp if exists
            pApp-OnFileExit();
      }
      // here put the code from your current CMainFrame::OnClose()

      // call baseclass function
      CGoMDIFrameWnd::OnClose();  
     
}


void CFoodApp::CloseDoc(CMultiDocTemplate* pTemplate, long objectID)

{

    POSITION    pos = pTemplate->GetFirstDocPosition(); 

    CFoodDoc*   pDoc;

    while (pos != NULL)

    {  

        pDoc = (CFoodDoc*)pTemplate->GetNextDoc(pos);

        if (pDoc != NULL && pDoc->GetObject() != NULL && pDoc->GetObject()->m_objectID == objectID)

        {

            POSITION posView = pDoc->GetFirstViewPosition();

            CView*   pView;

            while (posView != NULL)

            {

                pView = pDoc->GetNextView(posView);

                if (pView != NULL)

                    pView->GetParentFrame()->SendMessage(WM_CLOSE);

            }

        }

    }

}

Open in new window

0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
I made some researches for the WM_QUIT message in MFC source as I didn't remember all.


The message loop was in CWinThread::PumpMessage where there is code like

BOOL CWinThread::PumpMessage()
{
       ASSERT_VALID(this);
       if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))
      {
#ifdef _DEBUG
            if (afxTraceFlags & traceAppMsg)
      TRACE0("CWinThread::PumpMessage - Received WM_QUIT.\n");
            m_nDisablePumpCount++; // application must die
            // Note: prevents calling message loop things in 'ExitInstance'
            // will never be decremented
#endif
            return FALSE;
       }

The GetMessage would return FALSE when a WM_QUIT was received.

The WM_QUIT should be posted in CWnd::OnNcDestroy in case the mainframe window was destroyed

// WM_NCDESTROY is the absolute LAST message sent.
void CWnd::OnNcDestroy()
{
   // cleanup main and active windows
   CWinThread* pThread = AfxGetThread();
   if (pThread != NULL)
   {
      if (pThread->m_pMainWnd == this)    // <--- HERE IT HAPPENS
      {
         if (!afxContextIsDLL)
         {
            // shut down current thread if possible
            if (pThread != AfxGetApp() || AfxOleCanExitApp())
               AfxPostQuitMessage(0);           // <---- THAT ADDS WM_QUIT AT END OF QUEUE
         }
         pThread->m_pMainWnd = NULL;
      }
      if (pThread->m_pActiveWnd == this)
         pThread->m_pActiveWnd = NULL;
   }


If that doesn't work either the main frame window never was destroyed or the pThread->m_pMainWnd wasn't set properly.

Both should/could be found out by debugging into CMainFrame::OnClose.
 
0
 
LVL 1

Author Comment

by:psft
Comment Utility
I have tried out both of the suggestions in post ID 24875330, and neither helps.

I was able to successfully place a breakpoint in CWnd::OnNcDestroy.  It was called multiple times during the course of the shutdown as different CWnd objects were destroyed.  One of these cases did call AfxPostQuitMessage(0), but the application was still caught in the message loop afterwards.

The application is calling functions from an ActiveX control to handle the serial communications.  It is possible that something in there is not shutting down properly and preventing the application from closing?
0
 
LVL 44

Expert Comment

by:AndyAinscow
Comment Utility
>>It is possible that something in there is not shutting down properly and preventing the application from closing?

Yes -  what I said initially.
0
 
LVL 1

Author Comment

by:psft
Comment Utility
The ActiveX control was not mentioned earlier and it was not my intention to to offend.  How do I go about debugging the ActiveX control?  Does it have its own message loop?
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
>>>> Does it have its own message loop?
No, but it could hang in some final message handling directed to it and hence the WM_QUIT message was not processed. You could try to close the Active-X actively when catching the initial WM_CLOSE. Use SendMessage commands rather than PostMessage to get immediatee message handling rather than defered one. It may hang nevertheless depending on what it needs to properly shutdown but at least you should find out whether it is the reason for the hanging.
0
 
LVL 1

Author Comment

by:psft
Comment Utility
How does one close the ActiveX?  I can stop my program from making calls to its functions, but don't know how to stop it directly.
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
>>>> How does one close the ActiveX?  
First option is a Close or Destroy method if supplied by the control. If not you could try to send a WM_CLOSE or WM_DESTROY message to the active-x. But actually any Active-X must provide a method to orderly destroy it.
0
 
LVL 1

Accepted Solution

by:
psft earned 0 total points
Comment Utility
The cause was not from the ActiveX not shutting down, or the WM_QUIT not being received.  The code had a screen refresh function that would Dispatch messages while waiting for the I/O to complete.  This caused a logical block where the Dispatch was waiting for the WM_QUIT to finish being processed, but the I/O process was waiting for the refresh function containing the Dispatch to complete.  A check in the refresh function to ignore the message initiating the close corrected the problem.
0

Featured Post

Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
Running shell command from Access 3 63
Separate into files by filename 12 66
post4 challenge 28 81
sumHeights2  challenge 7 75
Here is how to use MFC's automatic Radio Button handling in your dialog boxes and forms.  Beginner programmers usually start with a OnClick handler for each radio button and that's just not the right way to go.  MFC has a very cool system for handli…
Introduction: Database storage, where is the exe actually on the disc? Playing a game selected randomly (how to generate random numbers).  Error trapping with try..catch to help the code run even if something goes wrong. Continuing from the seve…
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
When you create an app prototype with Adobe XD, you can insert system screens -- sharing or Control Center, for example -- with just a few clicks. This video shows you how. You can take the full course on Experts Exchange at http://bit.ly/XDcourse.

771 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now