PRB: MFC ActiveX Control Fails to Insert into PowerPoint 2000 (243240)



The information in this article applies to:

  • Microsoft PowerPoint 2000
  • The Microsoft Foundation Classes (MFC)
  • Microsoft Visual C++, 32-bit Professional Edition 6.0

This article was previously published under Q243240

SYMPTOMS

When trying to insert an MFC ActiveX Control into Microsoft PowerPoint 2000 by selecting Object from the Insert menu, you get the following error message:
PowerPoint found an error that it cannot correct, you should save presentations, quit, and then restart PowerPoint.

CAUSE

MFC tries to QueryInterface() for IOleControlSite in its implementation of IOleObject::SetClientSite(). However, Microsoft PowerPoint does not expect this situation for OLE objects inserted through Insert|Object, and reports the error.

RESOLUTION

Change your implementation to avoid calling QueryInterface() for IOleControlSite until after IOleObject::SetClientSite() is called.

MORE INFORMATION

The following steps describe how to override MFC's implementation of IOleObject for an MFC ActiveX Control, and move the QueryInterface() for IOleControlSite from SetClientSite() to SetHostNames(). An added benefit of overriding SetHostNames() is that your control can easily get the name of the container and containing document where it was inserted.
  1. Open an existing MFC ActiveX Control you may have with Insert|Object support, or create a new one for this example.
  2. Follow steps 3, 5, and 7 in the following Microsoft Knowledge Base article, replacing "COffCtlDispCtrl" with the name of your COleControl class instead. This allows you to override IOleObject:

    190985 HOWTO: Get IDispatch of an Excel or Word Document from an OCX

  3. Replace the implementation for SetClientSite() with the following code:
     // Change 'CMfcAxCtrlCtrl' in the next line to be the name of 
     // your COleControl class...
       METHOD_MANAGE_STATE(CMfcAxCtrlCtrl, MyOleObject)
       ASSERT_VALID(pThis);
    
     // maintain reference counts
       if (pClientSite != NULL)
          pClientSite->AddRef();
       if(pThis->m_pClientSite) {
          pThis->m_pClientSite->Release();
          pThis->m_pClientSite = NULL;
       }
       pThis->m_pClientSite = pClientSite;
    
       // Release existing pointer to ambient property dispinterface.
       pThis->m_ambientDispDriver.ReleaseDispatch();
    
       if (pClientSite != NULL)
       {
          BOOL bValue;
          pThis->m_bAutoClip = pThis->GetAmbientProperty(
             DISPID_AMBIENT_AUTOCLIP, VT_BOOL, &bValue) ? bValue : 0;
          pThis->m_bMsgReflect = pThis->GetAmbientProperty(
             DISPID_AMBIENT_MESSAGEREFLECT, VT_BOOL, &bValue) ? bValue : 0;
          pThis->m_bUIDead = (BYTE)(pThis->AmbientUIDead());
       }
    
     // Release existing pointer to in-place site, if any.
       if(pThis->m_pInPlaceSite) {
          pThis->m_pInPlaceSite->Release();
          pThis->m_pInPlaceSite = NULL;
       }
    
       LPVOID pInterface;
       if(pThis->m_pControlSite) {
          pThis->m_pControlSite->Release();
       }
       pThis->m_pControlSite = NULL;
    
       
     // Initialize pointer to simple frame site
       if (pClientSite == NULL || !pThis->m_bSimpleFrame ||
          FAILED(pClientSite->QueryInterface(IID_ISimpleFrameSite,
          &pInterface)))
       {
          pInterface = NULL;
       }
    
       if(pThis->m_pSimpleFrameSite) {
          pThis->m_pSimpleFrameSite->Release();
          pThis->m_pSimpleFrameSite = NULL;
       }
       pThis->m_pSimpleFrameSite = (LPSIMPLEFRAMESITE) pInterface;
    
     // Let the control run its own code here.
       pThis->OnSetClientSite();
    
     // Unless IPersist*::Load or IPersist*::InitNew is called after this,
     // we can't count on ambient properties being available while loading.
       pThis->m_bCountOnAmbients = FALSE;
    
       return S_OK;
    					
  4. Replace the implementation for SetHostNames() with the following:
     // Change 'CMfcAxCtrlCtrl' in the next line to be the name of
     // your COleControl class...
       METHOD_MANAGE_STATE(CMfcAxCtrlCtrl, MyOleObject)
       ASSERT_VALID(pThis);
    
     // Convert OLESTR into ASCII string.
       char app[1024], obj[1024];
       WideCharToMultiByte(CP_ACP, 0, pwApp, -1, app, 1024, NULL, NULL);
       WideCharToMultiByte(CP_ACP, 0, pwObj, -1, obj, 1024, NULL, NULL);
       
       char buf[1024];
       sprintf(buf, "SetHostNames(\"%s\", \"%s\") called", app, obj);
       ::MessageBox(NULL, buf, "MyControl", MB_SETFOREGROUND);
    
       // Get IOleControlSite
       if (pThis->m_pClientSite && !pThis->m_pControlSite) {
          pThis->m_pClientSite->QueryInterface(IID_IOleControlSite,
          (void**)&pThis->m_pControlSite);
       }
       
       return S_OK;
    					
    After moving the code that calls QueryInterface() for IOleControlSite from SetClientSite() to SetHostNames(), your control should insert into Microsoft PowerPoint without any errors. The example code above also demonstrates how you can discern the application and document name into which your control has been inserted using the arguments passed into SetHostNames().

Modification Type:MajorLast Reviewed:12/12/2003
Keywords:kbCtrlCreate kbprb KB243240