How to Avoid an MFC Active Document Container from Hanging

  • Click the Finish button
  • Creating an Active Document Server That Contains a Rich Edit Control

    1. Using Visual Studio, create an MFC project (any name will do for the purposes of this demo).
    2. Click mini-server and check Active document server checkbox.
    3. Select a CFormView instead of the CView.
    4. Click the Finish button.
    5. In the resource editor, drag a rich edit control to the form.
    6. Insert a call to AfxInitRichEdit() in the application object’s
      InitInstance function.
    7. Compile and run the server to get it registered in registry.

    Understanding the Problem

    Now that you’ve created a demo application, let’s see the problem up close.

    1. Run the test container application
    2. Select to inplace active an object.
    3. From the dialog, select the server you just created.
    4. Close the application
    5. The program hangs every time!

    How the Problem Can Be Solved

    Ok. Now we see there’s a proble. How can it be solved?

    In the OnCloseDocument method in the container the COleClientItem(COleDocObjectItem) object
    is released with a call to InternalRelease(). But since the member variable
    m_dwRef is 8 InternalRelease does not call OnFinalRelease and therefore the
    server is never totally released. For a server that does not contain a rich edit
    control m_dwRef is 1. Well, what can we do about that? The obvious is to call
    release on the COleClientItem until it is one, but that causes an
    exception in Ole32.dll, so that’s not the solution.

    I found a way to deal with the problem by adding some code to the OnClose method
    in the CMainFrame class. MFC uses an AFX_MODULE_STATE object to manage the MDI
    windows used in the container. During a normal CMDIFrameWnd::OnClose()
    call a call to CloseAllDocuments(…) is made, but if the m_nObjectCount in the AFX_MODULE_STATE is not 0 then the application is not closed down.
    What I did was to call CloseAllDocuments() before calling OnClose(), but before
    the call to OnClose is made the m_nObjectCount value
    is checked. If it’s not 0 then AfxOleUnlockApp() is called. In the OnClose()
    ObjectCount is now 0 and the call to AfxOleCanExitApp() in OnClose returns
    true which causes the application to shutdown!

    Code to Fix the Problem

    Here’s the code I used to finally fix this problem.

    void CMainFrame::OnClose()
    {
     AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
     TRACE("module count %d\n", pModuleState->m_nObjectCount);
    
     try
     {
      int incOleLock=0;
    
      // m_nObjectCount is telling how many mdi windows 
      // that are open (I guess)
    
      // This is called to make the objectCount go as low as possible.
      AfxGetApp()->CloseAllDocuments(FALSE);
      while(pModuleState->m_nObjectCount > 0)
      {
       // If we end up here then and object "hangs"
    
       // This is called BEFORE OnClose to make the OnClose destroy the window
       AfxOleUnlockApp();
    
       // This tells us to remember to increment the object count
       // AFTER the window is destroyed
       incOleLock++;
      }
      CMDIFrameWnd::OnClose();
      while(incOleLock)
      {
       // incerement ObjectCount to let the hanging object close
       // down itself.
       AfxOleLockApp();
    
       incOleLock--;
      }
     }
     catch(...)
     {
      TRACE("Exception thrown during void CMainFrame::OnClose() shutdown\n");
      }
    }
    

    Downloads

    Download source – 79 Kb

    More by Author

    Get the Free Newsletter!

    Subscribe to Developer Insider for top news, trends & analysis

    Must Read