/*--------------------------------------------------------------------------* * * Microsoft Windows * Copyright (C) Microsoft Corporation, 1992 - 2000 * * File: mmcaxwin.cpp * * Contents: functions for CMMCAxWindow * * History: 27-Jan-2000 audriusz Created * *--------------------------------------------------------------------------*/ #include "stdafx.h" #include "mshtml.h" #include "amc.h" #include "ocxview.h" #include "amcview.h" #include "findview.h" #ifdef DBG CTraceTag tagMMCViewBehavior (TEXT("MMCView Behavior"), TEXT("MMCView Behavior")); #endif /***************************************************************************\ * * METHOD: CMMCAxHostWindow::Invoke * * PURPOSE: ATL 3.0 has a bug in type library so we owerride this method to * take care of properties which will fail othervise * * PARAMETERS: * DISPID dispIdMember * REFIID riid * LCID lcid * WORD wFlags * DISPPARAMS FAR* pDispParams * VARIANT FAR* pVarResult * EXCEPINFO FAR* pExcepInfo * unsigned int FAR* puArgErr * * RETURNS: * HRESULT - result code * \***************************************************************************/ STDMETHODIMP CMMCAxHostWindow::Invoke( DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR* pDispParams, VARIANT FAR* pVarResult, EXCEPINFO FAR* pExcepInfo, unsigned int FAR* puArgErr) { DECLARE_SC(sc, TEXT("CMMCAxHostWindow::Invoke")); // This method is here to override IDispatch::Invoke from IDispatchImpl // to workaround the ATL30 bug - invalid type library entries for disp ids: // DISPID_AMBIENT_SHOWHATCHING and DISPID_AMBIENT_SHOWGRABHANDLES // Added to solve bug 453609 MMC2.0: ActiveX container: Painting problems with the device manager control if (DISPATCH_PROPERTYGET & wFlags) { if (dispIdMember == DISPID_AMBIENT_SHOWGRABHANDLES) { if (pVarResult == NULL) { sc = SC(E_INVALIDARG); return sc.ToHr(); } V_VT(pVarResult) = VT_BOOL; sc = get_ShowGrabHandles(&(V_BOOL(pVarResult))); return sc.ToHr(); } else if (dispIdMember == DISPID_AMBIENT_SHOWHATCHING) { if (pVarResult == NULL) { sc = SC(E_INVALIDARG); return sc.ToHr(); } V_VT(pVarResult) = VT_BOOL; sc = get_ShowHatching(&(V_BOOL(pVarResult))); return sc.ToHr(); } } // default: forward to base class return CAxHostWindow::Invoke( dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); } /***************************************************************************\ * * METHOD: CMMCAxHostWindow::OnPosRectChange * * PURPOSE: ATL does not implement this method, but it's needed to size MFC controls * * PARAMETERS: * LPCRECT lprcPosRect - rectangle to fit in * * RETURNS: * HRESULT - result code * \***************************************************************************/ STDMETHODIMP CMMCAxHostWindow::OnPosRectChange(LPCRECT lprcPosRect) { DECLARE_SC(sc, TEXT("CMMCAxHostWindow::OnPosRectChange")); // give base class a try (use temp sc to prevent tracing here) SC sc_temp = CAxHostWindow::OnPosRectChange(lprcPosRect); // we only want to come into the game as the last resort if (!(sc_temp == SC(E_NOTIMPL))) return sc_temp.ToHr(); // Added to solve bug 453609 MMC2.0: ActiveX container: Painting problems with the device manager control // since ATL does not implement it, we have to do it to make MFC controls happy // from MSDN: // When the in-place object calls IOleInPlaceSite::OnPosRectChange, // the container must call IOleInPlaceObject::SetObjectRects to specify // the new position of the in-place window and the ClipRect. // Only then does the object resize its window. // get pointer to control IDispatchPtr spExtendedControl; sc= GetExtendedControl(&spExtendedControl); if (sc) return sc.ToHr(); // get inplace object interface IOleInPlaceObjectPtr spInPlaceObject = spExtendedControl; if (spInPlaceObject == NULL) { sc = SC(E_UNEXPECTED); return sc.ToHr(); } sc = spInPlaceObject->SetObjectRects(lprcPosRect,lprcPosRect); if (sc) return sc.ToHr(); return sc.ToHr(); } /***************************************************************************\ * * METHOD: CMMCAxHostWindow::OnSetFocus * * PURPOSE: Simple override of bogus CAxHostWindow::OnSetFocus * Coppied from ATL 3.0, changed m_bInPlaceActive to m_bUIActive * See bug 433228 (MMC2.0 Can not tab in a SQL table) * * PARAMETERS: * UINT uMsg * WPARAM wParam * LPARAM lParam * BOOL& bHandled * * RETURNS: * SC - result code * \***************************************************************************/ LRESULT CMMCAxHostWindow::OnSetFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { m_bHaveFocus = TRUE; if (!m_bReleaseAll) { if (m_spOleObject != NULL && !m_bUIActive) { CComPtr spClientSite; GetControllingUnknown()->QueryInterface(IID_IOleClientSite, (void**)&spClientSite); if (spClientSite != NULL) { Trace (tagOCXActivation, _T("Activating in-place object")); HRESULT hr = m_spOleObject->DoVerb(OLEIVERB_UIACTIVATE, NULL, spClientSite, 0, m_hWnd, &m_rcPos); Trace (tagOCXActivation, _T("UI activation returned 0x%08x"), hr); } } if(!m_bWindowless && !IsChild(::GetFocus())) { Trace (tagOCXActivation, _T("Manually setting focus to first child")); ::SetFocus(::GetWindow(m_hWnd, GW_CHILD)); } } else Trace (tagOCXActivation, _T("Skipping UI activation")); /* * The code above might cause the focus to be sent elsewhere, which * means this window will receive WM_KILLFOCUS. CAxHostWindow::OnKillFocus * sets m_bHaveFocus to FALSE. * * If we set bHandled = FALSE here, then ATL will call CAxHostWindow::OnSetFocus, * which will set m_bHaveFocus to TRUE again, even though we've already * lost the focus. We only want to forward on to CAxHostWindow if * we still have the focus after attempting to activate our hosted control. */ if (m_bHaveFocus) { Trace (tagOCXActivation, _T("Forwarding to CAxHostWindow::OnSetFocus")); bHandled = FALSE; } else Trace (tagOCXActivation, _T("Skipping CAxHostWindow::OnSetFocus")); return 0; } /*+-------------------------------------------------------------------------* * class CMMCViewBehavior * * * PURPOSE: Allows the current snapin view (ie list, web, or OCX) to be * superimposed onto a view extension. The behavior can be attached * to any tag, and will cause the snapin view to display in the area * occupied by the tag. * *+-------------------------------------------------------------------------*/ class CMMCViewBehavior : public CComObjectRoot, public IElementBehavior, public IDispatch // used as the event sink { typedef CMMCViewBehavior ThisClass; UINT m_bCausalityCount; // fix to the bug #248351 - ntbug9. 6/25/01 "No List" taskpad displays a list when node selection changes from extended view // the script should not force the list to be shown more then once, since, due to the asynchronous nature of the // script execution, some of code may be executed late, after the MMC hides the listview. // In such case showing the listview is harmful bool m_bShowShowListView; public: BEGIN_COM_MAP(ThisClass) COM_INTERFACE_ENTRY(IElementBehavior) COM_INTERFACE_ENTRY(IDispatch) // NEEDED. See note above END_COM_MAP() DECLARE_NOT_AGGREGATABLE(ThisClass) // constructor CMMCViewBehavior() : m_pAMCView(NULL), m_bCausalityCount(0), m_bShowShowListView(true) {} // IElementBehavior STDMETHODIMP Detach() {return ScDetach().ToHr();} STDMETHODIMP Init(IElementBehaviorSite *pBehaviorSite) {return ScInit(pBehaviorSite).ToHr();} STDMETHODIMP Notify(LONG lEvent,VARIANT *pVar) {return ScNotify(lEvent).ToHr();} // IDispatch STDMETHODIMP GetTypeInfoCount(unsigned int * pctinfo) {return E_NOTIMPL;} STDMETHODIMP GetTypeInfo(unsigned int iTInfo, LCID lcid, ITypeInfo ** ppTInfo) {return E_NOTIMPL;} STDMETHODIMP GetIDsOfNames( REFIID riid, OLECHAR **rgszNames, unsigned int cNames, LCID lcid, DISPID * rgDispId){return E_NOTIMPL;} STDMETHODIMP Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, unsigned int *puArgErr) {return ScUpdateMMCView().ToHr();} private: /*+-------------------------------------------------------------------------* * * ScNotify * * PURPOSE: Handles the IElementBehavior::Notify method. * When we get the document ready notification we can get the document * and get the CAMCView window which will be cached for future use. * * PARAMETERS: * LONG lEvent : * * RETURNS: * SC * *+-------------------------------------------------------------------------*/ SC ScNotify(LONG lEvent) { DECLARE_SC(sc, TEXT("CMMCViewBehavior::ScNotify")); // When the whole document is loaded access it to get the CAMCView window. if (lEvent == BEHAVIOREVENT_DOCUMENTREADY ) { // get the HTML document from the element IDispatchPtr spDispatchDoc; sc = m_spElement->get_document(&spDispatchDoc); if(sc) return sc; // QI for the IOleWindow interface IOleWindowPtr spOleWindow = spDispatchDoc; sc = ScCheckPointers(spOleWindow, E_UNEXPECTED); if(sc) return sc; // Get the IE window and find the ancestor AMCView HWND hwnd = NULL; sc = spOleWindow->GetWindow(&hwnd); if(sc) return sc; hwnd = FindMMCView(hwnd); // find the ancestor mmcview if(hwnd==NULL) return (sc = E_UNEXPECTED); m_pAMCView = dynamic_cast(CWnd::FromHandle(hwnd)); sc = ScCheckPointers(m_pAMCView); // make sure we found a valid view. if (sc) return sc; } sc = ScUpdateMMCView(); // this sets up the view initially return sc; } /*+-------------------------------------------------------------------------* * * ScInit * * PURPOSE: Initializes the behavior. Connects the behavior to the onresize * and onreadystatechange events of the element it is attached to. * We can talk to the element but cannot access document until we * get document-ready notification in Notify method. * * PARAMETERS: * IElementBehaviorSite * pBehaviorSite : * * RETURNS: * SC * *+-------------------------------------------------------------------------*/ SC ScInit(IElementBehaviorSite *pBehaviorSite) { DECLARE_SC(sc, TEXT("CMMCViewBehavior::Init")); sc = ScCheckPointers(pBehaviorSite); if(sc) return sc; sc = pBehaviorSite->GetElement(&m_spElement); if(sc) return sc; IDispatchPtr spDispatch = this; // does the addref IHTMLElement2Ptr spElement2 = m_spElement; sc = ScCheckPointers(spElement2.GetInterfacePtr(), spDispatch.GetInterfacePtr()); if(sc) return sc; // set the onresize handler sc = spElement2->put_onresize(_variant_t(spDispatch.GetInterfacePtr())); if(sc) return sc; // set the onreadystatechange handler sc = spElement2->put_onreadystatechange(_variant_t(spDispatch.GetInterfacePtr())); if(sc) return sc; return sc; } /*+-------------------------------------------------------------------------* * * ScDetach * * PURPOSE: Detaches the behavior * * RETURNS: * SC * *+-------------------------------------------------------------------------*/ SC ScDetach() { DECLARE_SC(sc, TEXT("CMMCViewBehavior::ScDetach")); m_spElement = NULL; m_pAMCView = NULL; return sc; } /*+-------------------------------------------------------------------------* * class CCausalityCounter * * * PURPOSE: used to determine whether a function has resulted in a call back to itself on the same stack * * USAGE: Initialize with a variable that is set to zero. *+-------------------------------------------------------------------------*/ class CCausalityCounter // { UINT & m_bCounter; public: CCausalityCounter(UINT &bCounter) : m_bCounter(bCounter){++m_bCounter;} ~CCausalityCounter() {--m_bCounter;} bool HasReentered() { return (m_bCounter>1); } }; /*+-------------------------------------------------------------------------* * * ScUpdateMMCView * * PURPOSE: The callback for all events that the behavior is connected to. This * causes the size of the snapin view to be recomputed and displayed * * This method is also called by IDispatch::Invoke, which is called for mouse-in, * mouse-out events. So this method may be called after Detach in which case * m_pAMCView is NULL which is legal. * * PARAMETERS: None * * RETURNS: * SC * *+-------------------------------------------------------------------------*/ SC ScUpdateMMCView() { DECLARE_SC(sc, TEXT("CMMCViewBehavior::ScUpdateMMCView")); CCausalityCounter causalityCounter(m_bCausalityCount); if(causalityCounter.HasReentered()) return sc; // avoid re-entering the function from itself. sc = ScCheckPointers(m_spElement); if(sc) return sc; // See the note above. if (! m_pAMCView) return sc; long offsetTop = 0; long offsetLeft = 0; long offsetHeight = 0; long offsetWidth = 0; // get the coordinates of the element sc = m_spElement->get_offsetTop(&offsetTop); if(sc) return sc; sc = m_spElement->get_offsetLeft(&offsetLeft); if(sc) return sc; sc = m_spElement->get_offsetHeight(&offsetHeight); if(sc) return sc; sc = m_spElement->get_offsetWidth(&offsetWidth); if(sc) return sc; Trace(tagMMCViewBehavior, TEXT("Top: %d Left: %d Height: %d Width: %d"), offsetTop, offsetLeft, offsetHeight, offsetWidth); // set the coordinates. NOTE: replace by a single method call sc = m_pAMCView->ScSetViewExtensionFrame(m_bShowShowListView, offsetTop, offsetLeft, offsetTop + offsetHeight /*bottom*/, offsetLeft + offsetWidth /*right*/); m_bShowShowListView = false; return sc; } // data members private: IHTMLElementPtr m_spElement; CAMCView * m_pAMCView; }; /*+-------------------------------------------------------------------------* * class CElementBehaviorFactory * * * PURPOSE: Creates instances of the MMCView behavior * *+-------------------------------------------------------------------------*/ class CElementBehaviorFactory : public CComObjectRoot, public IElementBehaviorFactory, public IObjectSafetyImpl // required { typedef CElementBehaviorFactory ThisClass; public: BEGIN_COM_MAP(ThisClass) COM_INTERFACE_ENTRY(IElementBehaviorFactory) COM_INTERFACE_ENTRY(IObjectSafety) END_COM_MAP() public: // IElementBehaviorFactory STDMETHODIMP FindBehavior(BSTR bstrBehavior, BSTR bstrBehaviorUrl, IElementBehaviorSite *pSite, IElementBehavior **ppBehavior) { DECLARE_SC(sc, TEXT("CElementBehaviorFactory::FindBehavior")); sc = ScCheckPointers(ppBehavior); if(sc) return sc.ToHr(); // init out parameter *ppBehavior = NULL; if((bstrBehavior != NULL) && (wcscmp(bstrBehavior, L"mmcview")==0)) // requested the mmcview behavior { typedef CComObject t_behavior; t_behavior *pBehavior = NULL; sc = t_behavior::CreateInstance(&pBehavior); if(sc) return sc.ToHr(); *ppBehavior = pBehavior; if(!*ppBehavior) { delete pBehavior; return (sc = E_UNEXPECTED).ToHr(); } (*ppBehavior)->AddRef(); // addref for client return sc.ToHr(); } return E_FAIL; } }; /*+-------------------------------------------------------------------------* * * CMMCAxHostWindow::QueryService * * PURPOSE: If called with SID_SElementBehaviorFactory, returns a behavior * factory that implements the mmcview behavior * * PARAMETERS: * REFGUID rsid : * REFIID riid : * void** ppvObj : * * RETURNS: * STDMETHODIMP * *+-------------------------------------------------------------------------*/ STDMETHODIMP CMMCAxHostWindow::QueryService( REFGUID rsid, REFIID riid, void** ppvObj) { DECLARE_SC(sc, TEXT("CMMCAxHostWindow::QueryService")); typedef CAxHostWindow BC; if(rsid==SID_SElementBehaviorFactory) { if(m_spElementBehaviorFactory==NULL) { // create the object typedef CComObject t_behaviorFactory; t_behaviorFactory *pBehaviorFactory = NULL; sc = t_behaviorFactory::CreateInstance(&pBehaviorFactory); if(sc) return sc.ToHr(); m_spElementBehaviorFactory = pBehaviorFactory; // does the addref if(m_spElementBehaviorFactory==NULL) { delete pBehaviorFactory; return (sc = E_UNEXPECTED).ToHr(); } } sc = m_spElementBehaviorFactory->QueryInterface(riid, ppvObj); return sc.ToHr(); } HRESULT hr = BC::QueryService(rsid, riid, ppvObj); return hr; // do not want errors from BC to be traced }