Cannot Navigate Away from MFC Active Document in IE4
ID: Q177551
|
The information in this article applies to:
-
Microsoft Internet Explorer (Programming) versions 4.0, 4.01
-
The Microsoft Foundation Classes (MFC), used with:
-
Microsoft Visual C++, 32-bit Editions, versions 4.2, 4.2b, 5.0
SUMMARY
When you load a file into Internet Explorer 4.0 (IE4) that is associated
with an Active Document server that is written in MFC, you cannot navigate
away from the currently loaded page. IE4 will appear to start loading the
next page, the progress bar indicator will fill part way, and the IE logo
will begin spinning. However, IE4 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:
- 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) {}
}
- 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)
- Implement the interface map in implementation file for
CMyDocObjectServer.
BEGIN_INTERFACE_MAP(CMyDocObjectServer, CDocObjectServer)
INTERFACE_PART(CMyDocObjectServer, IID_IOleCommandTarget,
MyOleCommandTarget)
END_INTERFACE_MAP()
- 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);
}
- 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.
- Change the call to METHOD_PROLOGUE_EX from this:
METHOD_PROLOGUE_EX(CMyDocObjectServer, OleCommandTarget)
to this:
METHOD_PROLOGUE_EX(CMyDocObjectServer, MyOleCommandTarget)
- 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.
- Add this include to the header file for your COleServerDoc-derived
class:
#include "MyDocObjectServer.h"
- Change the default implementation of GetDocObjectServer() in your
CDocument-derived class to the following:
return new CMyDocObjectServer(this, pDocSite);
- 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);
};
- Add this include statement to MyDocObjectServer.cpp:
#include "MyOleCmdUI.h"
- 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.
- 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 from the Microsoft Software Library:
The following files are available for download from the Microsoft
Download Center. Click the file names below to download the files:
Axdocfix.exe For more information about how to download files from the Microsoft
Download Center, please visit the Download Center at the following Web
address
http://www.microsoft.com/downloads/search.asp
and then click How to use the Microsoft Download Center.© Microsoft Corporation 1997, All Rights Reserved. Contributions by Scott Roberts, Microsoft Corporation
Additional query words:
Keywords : kbfile kbIE kbIE400 kbIE401 kbMFC kbDSupport
Version : WINDOWS:4.0,4.01; winnt:4.2,4.2b,5.0
Platform : WINDOWS winnt
Issue type : kbinfo
|