windows-nt/Source/XPSP1/NT/admin/activec/conui/mmcaxwin.cpp
2020-09-26 16:20:57 +08:00

592 lines
18 KiB
C++

/*--------------------------------------------------------------------------*
*
* 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<IAxWinAmbientDispatch,..>
// 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<IOleClientSite> 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<CAMCView *>(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<CElementBehaviorFactory, INTERFACESAFE_FOR_UNTRUSTED_CALLER> // 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<CMMCViewBehavior> 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<CElementBehaviorFactory> 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
}