/*--------------------------------------------------------------------------*
 *
 *  Microsoft Windows
 *  Copyright (C) Microsoft Corporation, 1992 - 1999
 *
 *  File:      ocxview.cpp
 *
 *  Contents:  Implementation file for COCXHostView
 *
 *  History:   12-Dec-97 JeffRo     Created
 *
 *  This class is required to host OCX controls to fix focus problems.
 *  The MDI child frame window keeps track of its currently active view.
 *  When we're hosting OCX controls without this view and the OCX get the
 *  focus, the MDI child frame thinks the previously active view, usually
 *  the scope tree, is still the active view.  So if the user Alt-Tabs
 *  away from MMC and back, for instance, the scope tree will get the focus
 *  even though the OCX had the focus before.
 *
 *  We need this view to represent the OCX, which isn't a view, to the MDI
 *  child frame.
 *
 *--------------------------------------------------------------------------*/

#include "stdafx.h"
#include "amc.h"
#include "ocxview.h"
#include "amcview.h"


#ifdef DBG
CTraceTag  tagOCXActivation     (_T("OCX"), _T("Activation"));
CTraceTag  tagOCXTranslateAccel (_T("OCX"), _T("TranslateAccelerator"));
#endif


/*+-------------------------------------------------------------------------*
 * class COCXCtrlWrapper
 *
 *
 * PURPOSE: Maintains a pointer to a CMMCAxWindow as well as to the OCX in
 *          the window.
 *
 *+-------------------------------------------------------------------------*/
class COCXCtrlWrapper : public CComObjectRoot, public IUnknown
{
    typedef COCXCtrlWrapper ThisClass;
public:
    COCXCtrlWrapper() : m_pOCXWindow(NULL)
    {
    }

    ~COCXCtrlWrapper()
    {
        if(m_pOCXWindow && m_pOCXWindow->IsWindow())
            m_pOCXWindow->DestroyWindow();

        delete m_pOCXWindow;
    }

    BEGIN_COM_MAP(ThisClass)
        COM_INTERFACE_ENTRY(IUnknown)
    END_COM_MAP()

    DECLARE_NOT_AGGREGATABLE(ThisClass);

    SC  ScInitialize(CMMCAxWindow *pWindowOCX, IUnknown *pUnkCtrl) // initialize with the window that hosts the control
    {
        DECLARE_SC(sc, TEXT("COCXCtrlWrapper::ScInitialize"));
        sc = ScCheckPointers(pWindowOCX, pUnkCtrl);
        if(sc)
            return sc;

        m_pOCXWindow = pWindowOCX;
        m_spUnkCtrl  = pUnkCtrl;
        return sc;
    }

    SC  ScGetControl(IUnknown **ppUnkCtrl)
    {
        DECLARE_SC(sc, TEXT("COCXCtrlWrapper::ScGetData"));
        sc = ScCheckPointers(ppUnkCtrl);
        if(sc)
            return sc;

        *ppUnkCtrl   = m_spUnkCtrl;
        if(*ppUnkCtrl)
            (*ppUnkCtrl)->AddRef();
        return sc;
    }

   CMMCAxWindow *       GetAxWindow() {return m_pOCXWindow;}

private:
   CMMCAxWindow *       m_pOCXWindow; // handle to the window.
   CComPtr<IUnknown>    m_spUnkCtrl; // the IUnknown of the control
};



/////////////////////////////////////////////////////////////////////////////
// COCXHostView

IMPLEMENT_DYNCREATE(COCXHostView, CView)

COCXHostView::COCXHostView()  : m_pAMCView(NULL)
{
}

COCXHostView::~COCXHostView()
{
    m_pAMCView = NULL;
}

/*+-------------------------------------------------------------------------*
 *
 * COCXHostView::PreCreateWindow
 *
 * PURPOSE: Adds the WS_CLIPCHILDREN bit. This prevents the host window
 *          from overwriting the OCX.
 *
 * PARAMETERS:
 *    CREATESTRUCT& cs :
 *
 * RETURNS:
 *    BOOL
 *
 *+-------------------------------------------------------------------------*/
BOOL
COCXHostView::PreCreateWindow(CREATESTRUCT& cs)
{
    cs.style |=  WS_CLIPCHILDREN;
    // give base class a chance to do own job
    BOOL bOK = (CView::PreCreateWindow(cs));

    // register view class
    LPCTSTR pszViewClassName = g_szOCXViewWndClassName;

    // try to register window class which does not cause the repaint
    // on resizing (do it only once)
    static bool bClassRegistered = false;
    if ( !bClassRegistered )
    {
        WNDCLASS wc;
        if (::GetClassInfo(AfxGetInstanceHandle(), cs.lpszClass, &wc))
        {
            // Clear the H and V REDRAW flags
            wc.style &= ~(CS_HREDRAW | CS_VREDRAW);
            wc.lpszClassName = pszViewClassName;
            // Register this new class;
            bClassRegistered = AfxRegisterClass(&wc);
        }
    }

    // change window class to one which does not cause the repaint
    // on resizing if we successfully registered such
    if ( bClassRegistered )
        cs.lpszClass = pszViewClassName;

    return bOK;
}


/*+-------------------------------------------------------------------------*
 *
 * COCXHostView::GetAxWindow
 *
 * PURPOSE: Returns a pointer to the current AxWindow.
 *
 * RETURNS:
 *    CMMCAxWindow *
 *
 *+-------------------------------------------------------------------------*/
CMMCAxWindow *
COCXHostView::GetAxWindow()
{
    COCXCtrlWrapper *pOCXCtrlWrapper = dynamic_cast<COCXCtrlWrapper *>(m_spUnkCtrlWrapper.GetInterfacePtr());
    if(!pOCXCtrlWrapper)
        return (NULL);

    return pOCXCtrlWrapper->GetAxWindow();
}

CAMCView *
COCXHostView::GetAMCView()
{
    return m_pAMCView;
}


BEGIN_MESSAGE_MAP(COCXHostView, CView)
    //{{AFX_MSG_MAP(COCXHostView)
    ON_WM_SIZE()
    ON_WM_SETFOCUS()
    ON_WM_MOUSEACTIVATE()
    ON_WM_SETTINGCHANGE()
    ON_WM_CREATE()
    ON_WM_DESTROY()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// COCXHostView drawing

void COCXHostView::OnDraw(CDC* pDC)
{
    // this view should always be totally obscured by the OCX it is hosting
}

/////////////////////////////////////////////////////////////////////////////
// COCXHostView diagnostics

#ifdef _DEBUG
void COCXHostView::AssertValid() const
{
    CView::AssertValid();
}

void COCXHostView::Dump(CDumpContext& dc) const
{
    CView::Dump(dc);
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// COCXHostView message handlers
void COCXHostView::OnSize(UINT nType, int cx, int cy)
{
    ASSERT_VALID (this);
    CView::OnSize(nType, cx, cy);

    if (nType != SIZE_MINIMIZED)
    {
        if(GetAxWindow() != NULL)
            GetAxWindow()->MoveWindow (0, 0, cx, cy, FALSE /*bRepaint*/);
    }

}

void COCXHostView::OnSettingChange(UINT uFlags, LPCTSTR lpszSection)
{
	SetAmbientFont (NULL);

    CView::OnSettingChange(uFlags, lpszSection);

    if(GetAxWindow() != NULL)
        GetAxWindow()->SendMessage (WM_SETTINGCHANGE, uFlags, (LPARAM) lpszSection);
}


void COCXHostView::OnSetFocus(CWnd* pOldWnd)
{
    DECLARE_SC(sc, TEXT("COCXHostView::OnSetFocus"));

    ASSERT_VALID (this);

    // delegate the focus to the control we're hosting, if we have one
    if(GetAxWindow() != NULL)
       GetAxWindow()->SetFocus();

    // check if someone cared to take the focus.
    // default handling else.
    if (this == GetFocus())
    {
        CView::OnSetFocus (pOldWnd);
    }
}

int COCXHostView::OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest, UINT message)
{
    /*---------------------------------------------------------*/
    /* this code came from CView::OnMouseActivate; we do it    */
    /* here to bypass sending WM_MOUSEACTIVATE on the the      */
    /* parent window, avoiding focus churn in the parent frame */
    /*---------------------------------------------------------*/

    CFrameWnd* pParentFrame = GetParentFrame();
    if (pParentFrame != NULL)
    {
        // eat it if this will cause activation
        ASSERT(pParentFrame == pDesktopWnd || pDesktopWnd->IsChild(pParentFrame));

        // either re-activate the current view, or set this view to be active
        CView* pView = pParentFrame->GetActiveView();
        HWND hWndFocus = ::GetFocus();
        if (pView == this &&
            m_hWnd != hWndFocus && !::IsChild(m_hWnd, hWndFocus))
        {
            // re-activate this view
            OnActivateView(TRUE, this, this);
        }
        else
        {
            // activate this view
            pParentFrame->SetActiveView(this);
        }
    }
    return (MA_ACTIVATE);
}



BOOL COCXHostView::OnCmdMsg( UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo )
{
    // Do normal command routing
    if (CView::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
        return TRUE;

    // if view didn't handle it, give parent view a chance
    CWnd*   pParentView = GetParent ();

    if ((pParentView != NULL) &&
            pParentView->IsKindOf (RUNTIME_CLASS (CAMCView)) &&
            pParentView->OnCmdMsg (nID, nCode, pExtra, pHandlerInfo))
        return (TRUE);

    // not handled
    return FALSE;
}

void COCXHostView::OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView)
{
    DECLARE_SC(sc, TEXT("COCXHostView::OnActivateView"));

    CView::OnActivateView(bActivate,pActivateView,pDeactiveView);

    // If pActivateView and pDeactiveView are same then this app has lost
    // or gained focus without changing the active view within the app.
    // So do nothing.
    if (pActivateView == pDeactiveView)
        return;

    if (bActivate)
    {
        sc = ScFireEvent(COCXHostActivationObserver::ScOnOCXHostActivated);
        if (sc)
            sc.TraceAndClear();
    }
    else
    /*
     * If this view's no longer active, then the in-place object should
     * no longer be UI active.  This is important for the WebBrowser control
     * because if you move from one "Link to Web Address" node to another, or
     * from one taskpad to another, it won't allow tabbing to links on the
     * new hosted page if it's not deactivated and reactivated in the
     * appropriate sequence.
     */
    {
        IOleInPlaceObjectPtr spOleIPObj = GetIUnknown();

        /*
         * app hack for SQL snapin. Do not UIDeactivate the DaVinci control.
         * See bugs 175586, 175756, 193673 & 258109.
         */
        CAMCView *pAMCView = GetAMCView();
        sc = ScCheckPointers(pAMCView, E_UNEXPECTED);
        if (sc)
            return;

        SViewData *pViewData = pAMCView->GetViewData();
        sc = ScCheckPointers(pViewData, E_UNEXPECTED);
        if (sc)
            return;

        // If DaVinci control do not UIDeactivate.
        LPCOLESTR lpszOCXClsid = pViewData->GetOCX();
        if ( (_wcsicmp(lpszOCXClsid, L"{464EE255-FDC7-11D2-9743-00105A994F8D}") == 0) ||
			 (_wcsicmp(lpszOCXClsid, L"{97240642-F896-11D0-B255-006097C68E81}") == 0) )
            return;
        /*
         * app hack for SQL snapin ends here.
         */

        if (spOleIPObj != NULL)
        {
            Trace (tagOCXActivation, _T("Deactivating in-place object"));
            spOleIPObj->UIDeactivate();
        }
        else
            Trace (tagOCXActivation, _T("No in-place object to deactivate"));
    }
}

int COCXHostView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CView::OnCreate(lpCreateStruct) == -1)
        return -1;

    // initialize the AxWin class just once.
    static bool bIsAxWinInitialized = false;
    if(!bIsAxWinInitialized)
    {
        AtlAxWinInit();
        bIsAxWinInitialized = true;
    }

    // get a pointer to the AMCView.
    m_pAMCView = dynamic_cast<CAMCView*>(GetParent());

    return 0;
}

LPUNKNOWN COCXHostView::GetIUnknown(void)
{
    DECLARE_SC(sc, TEXT("COCXHostView::GetIUnknown"));

    COCXCtrlWrapper *pOCXCtrlWrapper = dynamic_cast<COCXCtrlWrapper *>((IUnknown *)m_spUnkCtrlWrapper);
    if(!pOCXCtrlWrapper)
    {
        sc = E_UNEXPECTED;
        return NULL;
    }

    IUnknownPtr spUnkCtrl;
    sc = pOCXCtrlWrapper->ScGetControl(&spUnkCtrl);
    if(sc)
        return NULL;

    return (LPUNKNOWN)spUnkCtrl;
}

/*+-------------------------------------------------------------------------*
 *
 * COCXHostView::ScSetControl
 *
 * PURPOSE: Hosts the specified control in the OCX view. Delegates to one of
 *          the two other overloaded versions of this function.
 *
 * PARAMETERS:
 *    HNODE           hNode :           The node that owns the view.
 *    CResultViewType& rvt:             The result view information
 *    INodeCallback * pNodeCallback :
 *
 * RETURNS:
 *    SC
 *
 *+-------------------------------------------------------------------------*/
SC
COCXHostView::ScSetControl(HNODE hNode, CResultViewType& rvt, INodeCallback *pNodeCallback)
{
    DECLARE_SC(sc, TEXT("COCXHostView::ScSetControl"));
    USES_CONVERSION;

    // make sure that we're trying to set up the right type of view.
    if(rvt.GetType() != MMC_VIEW_TYPE_OCX)
        return E_UNEXPECTED;

    // either BOTH rvt.IsPersistableViewDescriptionValid() and rvt.GetOCXUnknown() should be valid (the GetResultViewType2 case)
    // or     BOTH should be invalid and just GetOCX() should be valid.

    if(rvt.IsPersistableViewDescriptionValid() && (rvt.GetOCXUnknown() != NULL) )
    {
        // the GetResultViewType2 case
        sc = ScSetControl1(hNode, rvt.GetOCXUnknown(), rvt.GetOCXOptions(), pNodeCallback);
        if(sc)
            return sc;
    }
    else if(rvt.GetOCX() != NULL)
    {
        sc = ScSetControl2(hNode, rvt.GetOCX(),        rvt.GetOCXOptions(), pNodeCallback);
        if(sc)
            return sc;
    }
    else
    {
        // should never happen.
        return (sc = E_UNEXPECTED);
    }


    // must have a legal Ax Window at this point.
    sc = ScCheckPointers(GetAxWindow());
    if(sc)
        return sc;


    // the OCX should fill the entirety of the OCX host view
    CRect   rectHost;
    GetClientRect (rectHost);
    GetAxWindow()->SetWindowPos(HWND_TOP, rectHost.left, rectHost.top, rectHost.Width(), rectHost.Height(), SWP_NOACTIVATE | SWP_SHOWWINDOW);

    return sc;

}


/*+-------------------------------------------------------------------------*
 *
 * COCXHostView::ScSetControl1
 *
 * PURPOSE: Hosts the control specified by pUnkCtrl in the OCX view. Takes
 *          care of caching the control
 *
 * PARAMETERS:
 *    HNODE           hNode :
 *    LPUNKNOWN       pUnkCtrl :
 *    DWORD           dwOCXOptions :
 *    INodeCallback * pNodeCallback :
 *
 * RETURNS:
 *    SC
 *
 *+-------------------------------------------------------------------------*/
SC
COCXHostView::ScSetControl1(HNODE hNode, LPUNKNOWN pUnkCtrl, DWORD dwOCXOptions, INodeCallback *pNodeCallback)
{
    DECLARE_SC(sc, TEXT("COCXHostView::ScSetControl1"));

    // validate parameters.
    sc = ScCheckPointers((void *)hNode, pUnkCtrl, pNodeCallback);
    if(sc)
        return sc;

    CComPtr<IUnknown> spUnkCtrl;

    // 1. Hide existing window, if any.
    sc = ScHideWindow();
    if(sc)
        return sc;

    // 2. Get a cached window if one exists - NOTE that in this overload we do not look at RVTI_OCX_OPTIONS_CACHE_OCX at this point.
    sc = pNodeCallback->GetControl(hNode, pUnkCtrl, &m_spUnkCtrlWrapper);  // the overloaded form of GetControl
    if (sc)
        return sc;

    // 3. if no cached window, create one.
    if(m_spUnkCtrlWrapper == NULL) /*no cached window, create one*/
    {
        CMMCAxWindow * pWndAx = NULL;

        sc = ScCreateAxWindow(pWndAx);
        if(sc)
            return sc;

        CComPtr<IUnknown> spUnkContainer;

        // attach the container to the AxWindow
        sc = pWndAx->AttachControl(pUnkCtrl, &spUnkContainer);
        if(sc)
            return sc;


        // create a wrapper for the control
        CComObject<COCXCtrlWrapper> *pOCXCtrlWrapper = NULL;
        sc = CComObject<COCXCtrlWrapper>::CreateInstance(&pOCXCtrlWrapper);
        if(sc)
            return sc;

        spUnkCtrl = pUnkCtrl;

        // initialize the wrapper.
        // The pointer to the control and the CMMCAxWindow is now owned by the wrapper.
        sc = pOCXCtrlWrapper->ScInitialize(pWndAx, spUnkCtrl);
        if(sc)
            return sc;

        m_spUnkCtrlWrapper = pOCXCtrlWrapper; // does the addref.


        // cache only if the snapin asked us to. NOTE that this logic is different from the other version of SetControl
        if(dwOCXOptions &  RVTI_OCX_OPTIONS_CACHE_OCX)
        {
            // This is cached by the static node and used for all nodes of the snapin.
            sc = pNodeCallback->SetControl(hNode, pUnkCtrl, m_spUnkCtrlWrapper); // this call passes the wrapper
            if(sc)
                return sc;
        }

        // Do not send MMCN_INITOCX, the snapin created this control it should have initialized it.
    }
    else
    {
        // The next call sets m_spUnkCtrlWrapper, which is used to get a pointer to the Ax window.
        COCXCtrlWrapper *pOCXCtrlWrapper = dynamic_cast<COCXCtrlWrapper *>((IUnknown *)m_spUnkCtrlWrapper);
        if(!pOCXCtrlWrapper)
            return (sc = E_UNEXPECTED); // this should never happen.

        sc = pOCXCtrlWrapper->ScGetControl(&spUnkCtrl);
        if(sc)
            return sc;

        sc = ScCheckPointers(GetAxWindow(), (LPUNKNOWN)spUnkCtrl);
        if(sc)
            return sc;

        // un-hide the window.
        GetAxWindow()->ShowWindow(SW_SHOWNORMAL);

    }


    return sc;
}



/*+-------------------------------------------------------------------------*
 *
 * COCXHostView::ScSetControl2
 *
 * PURPOSE: Hosts the specified control in the OCX view. This is the
 *          OCX returned by GetResultViewType. Also takes care of
 *          caching the control if needed and sending the MMCN_INITOCX
 *          notification to snap-ins. The caching is done by hiding the
 *          OCX window and passing nodemgr a COM object that holds a pointer
 *          to the window as well as the control. The nodemgr side determines
 *          whether or not to cache the control. If the control is not
 *          cached, nodemgr merely releases the object passed to it.
 *
 * PARAMETERS:
 *    HNODE           hNode :
 *    LPCWSTR         szOCXClsid :
 *    DWORD           dwOCXOptions :
 *    INodeCallback * pNodeCallback :
 *
 * RETURNS:
 *    SC
 *
 *+-------------------------------------------------------------------------*/
SC
COCXHostView::ScSetControl2(HNODE hNode, LPCWSTR szOCXClsid, DWORD dwOCXOptions, INodeCallback *pNodeCallback)
{
    DECLARE_SC(sc, TEXT("COCXHostView::ScSetControl2"));

    // validate parameters.
    sc = ScCheckPointers((void *)hNode, szOCXClsid, pNodeCallback);
    if(sc)
        return sc;

    // create the OCX if needed
    CLSID clsid;
    sc = CLSIDFromString (const_cast<LPWSTR>(szOCXClsid), &clsid);
    if(sc)
        return sc;

    CComPtr<IUnknown> spUnkCtrl;

    sc = ScHideWindow();
    if(sc)
        return sc;

    // check whether there is a cached control for this node.
    if (dwOCXOptions &  RVTI_OCX_OPTIONS_CACHE_OCX)
    {
        sc = pNodeCallback->GetControl(hNode, clsid, &m_spUnkCtrlWrapper);
        if (sc)
            return sc;
    }

    // nope, create a control and set this control for the node.
    if (m_spUnkCtrlWrapper == NULL)
    {
        CMMCAxWindow * pWndAx = NULL;

        sc = ScCreateAxWindow(pWndAx);
        if(sc)
            return sc;

        sc = pWndAx->CreateControlEx(szOCXClsid, NULL /*pStream*/,
                                            NULL /*ppUnkContainer*/, &spUnkCtrl);
        if(sc)
            return sc;


        // spUnkCtrl should be valid at this point.
        sc = ScCheckPointers(spUnkCtrl);
        if(sc)
            return sc;

        CComObject<COCXCtrlWrapper> *pOCXCtrlWrapper = NULL;
        sc = CComObject<COCXCtrlWrapper>::CreateInstance(&pOCXCtrlWrapper);
        if(sc)
            return sc;

        sc = ScCheckPointers(pOCXCtrlWrapper);
        if(sc)
            return sc;

        // initialize the wrapper.
        // The pointer to the control and the CMMCAxWindow is now owned by the wrapper.
        sc = pOCXCtrlWrapper->ScInitialize(pWndAx, spUnkCtrl);
        if(sc)
            return sc;

        m_spUnkCtrlWrapper = pOCXCtrlWrapper; // does the addref.

        // This is cached by the static node and used for all nodes of the snapin.
        if (dwOCXOptions &  RVTI_OCX_OPTIONS_CACHE_OCX)
        {
            sc = pNodeCallback->SetControl(hNode, clsid, m_spUnkCtrlWrapper); // this call passes the wrapper
            if(sc)
                return sc;
        }

        // send the MMCN_INITOCX notification.
        sc = pNodeCallback->InitOCX(hNode, spUnkCtrl); // this passes the actual IUnknown of the control.
        if(sc)
            return sc;
    }
    else
    {
        // The next call sets m_spUnkCtrlWrapper, which is used to get a pointer to the Ax window.
        COCXCtrlWrapper *pOCXCtrlWrapper = dynamic_cast<COCXCtrlWrapper *>((IUnknown *)m_spUnkCtrlWrapper);
        if(!pOCXCtrlWrapper)
            return (sc = E_UNEXPECTED); // this should never happen.

        sc = pOCXCtrlWrapper->ScGetControl(&spUnkCtrl);
        if(sc)
            return sc;

        sc = ScCheckPointers(GetAxWindow(), (LPUNKNOWN)spUnkCtrl);
        if(sc)
            return sc;

        // un-hide the window.
        GetAxWindow()->ShowWindow(SW_SHOWNORMAL);

    }

    return sc;
}

/*+-------------------------------------------------------------------------*
 *
 * COCXHostView::ScHideWindow
 *
 * PURPOSE: Hides the existing window, if any.
 *
 * RETURNS:
 *    SC
 *
 *+-------------------------------------------------------------------------*/
SC
COCXHostView::ScHideWindow()
{
    DECLARE_SC(sc, TEXT("COCXCtrlWrapper::ScHideWindow"));

    // if there is an existing window, hide it.
    if(GetAxWindow())
    {
        GetAxWindow()->ShowWindow(SW_HIDE);
        m_spUnkCtrlWrapper.Release(); // this deletes the unneeded window if the reference count is zero.
    }


    return sc;
}

/*+-------------------------------------------------------------------------*
 *
 * COCXHostView::ScCreateAxWindow
 *
 * PURPOSE: Creates a new Ax window
 *
 * PARAMETERS:
 *    PMMCAXWINDOW  pWndAx :
 *
 * RETURNS:
 *    SC
 *
 *+-------------------------------------------------------------------------*/
SC
COCXHostView::ScCreateAxWindow(PMMCAXWINDOW &pWndAx)
{
    DECLARE_SC(sc, TEXT("COCXHostView::ScCreateAxWindow"));

    // create a new window
    pWndAx = new CMMCAxWindow;
    if(!pWndAx)
        return (sc = E_OUTOFMEMORY);


    // create the OCX host window
    RECT rcClient;
    GetClientRect(&rcClient);
    HWND hwndAx = pWndAx->Create(m_hWnd, rcClient, _T(""), (WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS) );

    if (hwndAx == NULL)
    {
        sc.FromLastError();
        return (sc);
    }

    /*
     * Bug 451981:  By default, the ATL OCX host window supports hosting
     * windowless controls.  This differs from the MMC 1.2 implementation
     * of the OCX host window (which used MFC), which did not.  Some controls
     * (e.g. Disk Defragmenter OCX) claim to support windowless instantiation
     * but do not.
     *
     * For compatibility, we must only instantiate result pane OCX's as
     * windowed controls.
     */
    CComPtr<IAxWinAmbientDispatch> spHostDispatch;
    sc = pWndAx->QueryHost(IID_IAxWinAmbientDispatch, (void**)&spHostDispatch);
    if (sc)
        sc.Clear();     // ignore this failure
    else
	{
        spHostDispatch->put_AllowWindowlessActivation (VARIANT_FALSE);  // disallow windowless activation
		SetAmbientFont (spHostDispatch);
	}

    return sc;
}


void COCXHostView::OnDestroy()
{
    CView::OnDestroy();

    if(GetAxWindow())
        GetAxWindow()->DestroyWindow();
}


/*+-------------------------------------------------------------------------*
 * COCXHostView::SetAmbientFont
 *
 * This function sets the font that any OCX that uses the DISPID_AMBIENT_FONT
 * ambient property will inherit.
 *--------------------------------------------------------------------------*/

void COCXHostView::SetAmbientFont (IAxWinAmbientDispatch* pHostDispatch)
{
	DECLARE_SC (sc, _T("COCXHostView::SetAmbientFont"));
    CComPtr<IAxWinAmbientDispatch> spHostDispatch;

	/*
	 * no host dispatch interface supplied?  get it from the AxWindow
	 */
	if (pHostDispatch == NULL)
	{
		CMMCAxWindow* pWndAx = GetAxWindow();
		if (pWndAx == NULL)
			return;

		sc = pWndAx->QueryHost(IID_IAxWinAmbientDispatch, (void**)&spHostDispatch);
		if (sc)
			return;

		pHostDispatch = spHostDispatch;
		sc = ScCheckPointers (pHostDispatch, E_UNEXPECTED);
		if (sc)
			return;
	}

	/*
	 * get the icon title font
	 */
    LOGFONT lf;
    SystemParametersInfo (SPI_GETICONTITLELOGFONT, sizeof(lf), &lf, false);

	/*
	 * get the desktop resolution
	 */
	CWindowDC dcDesktop (CWnd::GetDesktopWindow());
	int ppi = dcDesktop.GetDeviceCaps (LOGPIXELSY);
	long lfHeight = (lf.lfHeight >= 0) ? lf.lfHeight : -lf.lfHeight;

	/*
	 * create an IFontDisp interface around the icon title font
	 */
	USES_CONVERSION;
	FONTDESC fd;
	fd.cbSizeofstruct = sizeof (fd);
	fd.lpstrName      = T2OLE (lf.lfFaceName);
	fd.sWeight        = (short) lf.lfWeight;
	fd.sCharset       = lf.lfCharSet;
	fd.fItalic        = lf.lfItalic;
	fd.fUnderline     = lf.lfUnderline;
	fd.fStrikethrough = lf.lfStrikeOut;
	fd.cySize.Lo      = lfHeight * 720000 / ppi;
	fd.cySize.Hi      = 0;

	CComPtr<IFontDisp> spFontDisp;
	sc = OleCreateFontIndirect (&fd, IID_IFontDisp, (void**) &spFontDisp);
	if (sc)
		return;

	/*
	 * set the Font property on the AxHostWindow
	 */
    pHostDispatch->put_Font (spFontDisp);
}


/*+-------------------------------------------------------------------------*
 *
 * COCXHostView::PreTranslateMessage
 *
 * PURPOSE: Sends accelerator messages to the OCX.
 *
 * PARAMETERS:
 *    MSG* pMsg :
 *
 * RETURNS:
 *    BOOL
 *
 *+-------------------------------------------------------------------------*/
BOOL
COCXHostView::PreTranslateMessage(MSG* pMsg)
{
    if (pMsg->message >= WM_KEYFIRST && pMsg->message <= WM_KEYLAST)
    {
        IOleInPlaceActiveObjectPtr spOleIPAObj = GetIUnknown();

#ifdef DBG
        TCHAR szTracePrefix[32];
        wsprintf (szTracePrefix, _T("msg=0x%04x, vkey=0x%04x:"), pMsg->message, pMsg->wParam);
#endif

        if (spOleIPAObj != NULL)
        {
            bool fHandled = (spOleIPAObj->TranslateAccelerator(pMsg) == S_OK);
            Trace (tagOCXTranslateAccel, _T("%s %s handled"), szTracePrefix, fHandled ? _T("   ") : _T("not"));

            if (fHandled)
                return TRUE;
        }
        else
            Trace (tagOCXTranslateAccel, _T("%s not handled (no IOleInPlaceActiveObject*)"), szTracePrefix);
    }

    return BC::PreTranslateMessage(pMsg);
}