Cannot Navigate Away from MFC Active Document in Internet Explorer 4.0 (177551)



The information in this article applies to:

  • Microsoft Internet Explorer (Programming) 4.0
  • Microsoft Internet Explorer (Programming) 4.01
  • The Microsoft Foundation Classes (MFC), when used with:
    • Microsoft Visual C++, 32-bit Enterprise Edition 4.2b
    • Microsoft Visual C++, 32-bit Enterprise Edition 5.0
    • Microsoft Visual C++, 32-bit Professional Edition 4.2b
    • Microsoft Visual C++, 32-bit Professional Edition 5.0

This article was previously published under Q177551

SUMMARY

When you load a file into Internet Explorer 4.0 that is associated with an Active Document server that is written in MFC, you cannot navigate away from the currently loaded page. Internet Explorer 4.0 will appear to start loading the next page, the progress bar indicator will fill part way, and the Internet Explorer logo will begin spinning. However, Internet Explorer 4.0 will remain in this state indefinitely until shut down.

This is caused by a fault in MFC's implementation of IOleCommandTarget::QueryStatus. MFC's QueryStatus implementation incorrectly specifies that unknown command IDs are currently supported but disabled. Internet Explorer 4.0 uses an unsupported command-group command to determine whether the Active Document (formerly called "ActiveX Document") server can be deactivated. Since MFC responds incorrectly that the command-group command is supported and disabled, the Active Document server is never unloaded.

As a workaround, you must override CDocObjectServer and COleCmdUI to provide the correct functionality. (See the MORE INFORMATION section for instructions on how to do this.)

Microsoft has confirmed this to be a bug in the Microsoft products listed at the beginning of this article. We are researching this bug and will post new information here in the Microsoft Knowledge Base as it becomes available.

MORE INFORMATION

The following information describes the steps to work around the bug that is stated above. The AxDocFix sample referenced below demonstrates this workaround.

To work around this bug, follow these steps for a basic AppWizard-created Active Document server:
  1. Override CDocObjectServer: create a new class CMyDocObjectServer and derive it publicly from CDocObjectServer. Provide a constructor as follows:
          class CMyDocObjectServer : public CDocObjectServer
          {
          public:
             CMyDocObjectServer(COleServerDoc* pOwner,
                                LPOLEDOCUMENTSITE pDocSite = NULL)
                : CDocObjectServer(pOwner, pDocSite) {}
          }
    					
  2. Include the needed interface macros in the class definition for CMyDocObjectServer:
          DECLARE_INTERFACE_MAP()
          BEGIN_INTERFACE_PART(MyOleCommandTarget, IOleCommandTarget)
             STDMETHOD(QueryStatus)(const GUID*, ULONG, OLECMD[], OLECMDTEXT*);
             STDMETHOD(Exec)(const GUID*, DWORD, DWORD, VARIANTARG*,
                             VARIANTARG*);
          END_INTERFACE_PART(MyOleCommandTarget)
    					
  3. Implement the interface map in implementation file for CMyDocObjectServer:
    BEGIN_INTERFACE_MAP(CMyDocObjectServer, CDocObjectServer)
             INTERFACE_PART(CMyDocObjectServer, IID_IOleCommandTarget,
                            MyOleCommandTarget)
          END_INTERFACE_MAP()
    					
  4. Provide the following implementations for AddRef(), Release(), QueryInterface(), and Exec():
          STDMETHODIMP_(ULONG)
               CMyDocObjectServer::XMyOleCommandTarget::AddRef()
          {
             METHOD_PROLOGUE_EX(CMyDocObjectServer, MyOleCommandTarget)
             ASSERT_VALID(pThis);
    
             return pThis->m_xOleCommandTarget.AddRef();
          }
    
          STDMETHODIMP_(ULONG)
                        CMyDocObjectServer::XMyOleCommandTarget::Release()
          {
             METHOD_PROLOGUE_EX(CMyDocObjectServer, MyOleCommandTarget)
             ASSERT_VALID(pThis);
    
             return pThis->m_xOleCommandTarget.Release();
          }
    
          STDMETHODIMP CMyDocObjectServer::XMyOleCommandTarget::QueryInterface(
                REFIID iid, LPVOID* ppvObj)
          {
             METHOD_PROLOGUE_EX(CMyDocObjectServer, MyOleCommandTarget)
             ASSERT_VALID(pThis);
    
             return pThis->m_pOwner->ExternalQueryInterface(&iid, ppvObj);
          }
    
          STDMETHODIMP CMyDocObjectServer::XMyOleCommandTarget::Exec(
                const GUID* pguidCmdGroup, DWORD nCmdID, DWORD nCmdExecOpt,
                VARIANTARG* pvarargIn, VARIANTARG* pvarargOut)
          {
             METHOD_PROLOGUE_EX(CMyDocObjectServer, MyOleCommandTarget)
             ASSERT_VALID(pThis);
    
             return pThis-&m_xOleCommandTarget.Exec(pguidCmdGroup, nCmdID,
                                                    nCmdExecOpt, pvarargIn,
                                                    pvarargOut);
          }
    					
  5. Copy the code for QueryStatus() from mfc\src\oledoctg.cpp. Change the signature for this method in MyDocObjectServer.cpp to the following:
    STDMETHODIMP CMyDocObjectServer::XMyOleCommandTarget::QueryStatus(
             const GUID* pguidCmdGroup, ULONG cCmds, OLECMD rgCmds[],
             OLECMDTEXT* pcmdtext)
          {
          ...
          }
    						
    This changes the nested class name from XOleCommandTarget to XMyOleCommandTarget.
  6. Change the call to METHOD_PROLOGUE_EX from this:
    METHOD_PROLOGUE_EX(CMyDocObjectServer, OleCommandTarget)
    						
    to this:
          METHOD_PROLOGUE_EX(CMyDocObjectServer, MyOleCommandTarget)
        
    					
  7. Change the 18th line in CDocObjectServer::XOleCommandTarget::QueryStatus (first line after the second else statement) from this
    COleCmdUI state(rgCmds, cCmds, pguidCmdGroup);
    						
    to this
    CMyOleCmdUI state(rgCmds, cCmds, pguidCmdGroup);
    						
    CMyOleCmdUI is the COleCmdUI-derived class that you will create in step 10.
  8. Add this include to the header file for your COleServerDoc-derived class:
          #include "MyDocObjectServer.h"
    						
  9. Change the default implementation of GetDocObjectServer() in your CDocument-derived class to the following:
         return new CMyDocObjectServer(this, pDocSite);
    					
  10. Override COleCmdUI: create a new class CMyOleCmdUI and derive it publicly from COleCmdUI. Override the virtual function DoUpdate():
          class CMyOleCmdUI : public COleCmdUI
          {
           public:
             CMyOleCmdUI(OLECMD* rgCmds, ULONG cCmds, const GUID* pGroup)
                : COleCmdUI(rgCmds, cCmds, pGroup)
             {
             }
    
             virtual BOOL DoUpdate(CCmdTarget* pTarget, BOOL
                                   bDisableIfNoHandler);
          };
  11. Add this include statement to MyDocObjectServer.cpp:
          #include "MyOleCmdUI.h"
    					
  12. Cut and paste the implementation for DoUpdate() from Mfc\Src\Oledoctg.cpp (line# 64 in Visual C++ 5.0.) Change this code in your CMyOleCmdUI::DoUpdate:
          if (bDisableIfNoHandler && !m_bEnableChanged)
          {
             AFX_CMDHANDLERINFO info;
             info.pTarget = NULL;
             bResult = pTarget->OnCmdMsg(m_nID, CN_COMMAND, this, &info);
    
             Enable(bResult);
             if (bResult || m_bEnableChanged)
                m_rgCmds[m_nIndex].cmdf |= OLECMDF_SUPPORTED;
             else
                m_rgCmds[m_nIndex].cmdf &= ~OLECMDF_SUPPORTED;
          }
    						
    to this:
          if (bDisableIfNoHandler && !m_bEnableChanged)
          {
             AFX_CMDHANDLERINFO info;
             info.pTarget = NULL;
             bResult = pTarget->OnCmdMsg(m_nID, CN_COMMAND, this, &info);
    
             if (bResult || m_bEnableChanged)
                m_rgCmds[m_nIndex].cmdf |= OLECMDF_SUPPORTED;
             else
                m_rgCmds[m_nIndex].cmdf &= ~OLECMDF_SUPPORTED;
             Enable(bResult);
          }
    						
    This changes the order in which Enable is called.
  13. Add this include statement to the CMyDocObjectServer implementation file:
          #include "afxconv.h"
    						
    and add this include statement to the CMyOleCmdUI implementation file:
          #include <afxpriv.h>
    					
Afxpriv.h is needed for the proper implementation of CMyOleCmdUI's command routing mechanism in DoUpdate(). NOTE: Code that requires Afxpriv.h is not guaranteed to work correctly in future versions of Visual C++ and with future versions of the MFC DLL. However, because this is the only workaround to the bug described in this article, use of Afxpriv.h cannot be avoided. Once a future version of MFC has fixed this bug, this workaround should be removed from your code.

A default MFC Active Document server that has these fixes applied is available for download. The following files are available for download from the Microsoft Download Center:
For additional information about how to download Microsoft Support files, click the following article number to view the article in the Microsoft Knowledge Base:

119591 How to Obtain Microsoft Support Files from Online Services

Microsoft scanned this file for viruses. Microsoft used the most current virus-detection software that was available on the date that the file was posted. The file is stored on security-enhanced servers that help to prevent any unauthorized changes to the file.

Modification Type:MinorLast Reviewed:8/5/2004
Keywords:kbdownload kbfile kbinfo kbSample KB177551 kbAudDeveloper