windows-nt/Source/XPSP1/NT/shell/shdocvw/shocx.cpp
2020-09-26 16:20:57 +08:00

337 lines
9.3 KiB
C++

// builds an ocx out of the embedding in shembed.c
#include "priv.h"
#include "sccls.h"
#include "olectl.h"
#include "stdenum.h"
#include "shocx.h"
#include "resource.h"
LCID g_lcidLocale = MAKELCID(LANG_USER_DEFAULT, SORT_DEFAULT);
#define SUPERCLASS CShellEmbedding
CShellOcx::CShellOcx(IUnknown* punkOuter, LPCOBJECTINFO poi, const OLEVERB* pverbs, const OLEVERB* pdesignverbs) :
CShellEmbedding(punkOuter, poi, pverbs),
_pDesignVerbs(pdesignverbs),
CImpIDispatch(LIBID_SHDocVw, 1, 1, *(poi->piid))
{
// CShellEmbedding class handles the DllAddRef / DllRelease
m_cpEvents.SetOwner(_GetInner(), poi->piidEvents);
m_cpPropNotify.SetOwner(_GetInner(), &IID_IPropertyNotifySink);
_nDesignMode = MODE_UNKNOWN;
}
CShellOcx::~CShellOcx()
{
// Should have been released when cllient site was set to NULL.... Don't release
// it here as this will cause some applications like VC5 to fault...
ASSERT(_pDispAmbient==NULL);
if (_pClassTypeInfo)
_pClassTypeInfo->Release();
}
//
// We have a different set of verbs in design mode
//
HRESULT CShellOcx::EnumVerbs(IEnumOLEVERB **ppEnumOleVerb)
{
TraceMsg(TF_SHDCONTROL, "sho: EnumVerbs");
if (_IsDesignMode())
{
*ppEnumOleVerb = new CSVVerb(_pDesignVerbs);
if (*ppEnumOleVerb)
return S_OK;
}
return SUPERCLASS::EnumVerbs(ppEnumOleVerb);
}
//
// For the interfaces we support here
//
HRESULT CShellOcx::v_InternalQueryInterface(REFIID riid, void **ppvObj)
{
static const QITAB qit[] = {
QITABENT(CShellOcx, IDispatch),
QITABENT(CShellOcx, IOleControl),
QITABENT(CShellOcx, IConnectionPointContainer),
QITABENT(CShellOcx, IPersistStreamInit),
QITABENTMULTI(CShellOcx, IPersistStream, IPersistStreamInit),
QITABENT(CShellOcx, IPersistPropertyBag),
QITABENT(CShellOcx, IProvideClassInfo2),
QITABENTMULTI(CShellOcx, IProvideClassInfo, IProvideClassInfo2),
{ 0 },
};
HRESULT hr = QISearch(this, qit, riid, ppvObj);
if (FAILED(hr))
hr = SUPERCLASS::v_InternalQueryInterface(riid, ppvObj);
return hr;
}
//
// On a SetClientSite, we need to discard everything created from _pcli
// because shembed frees _pcli
//
HRESULT CShellOcx::SetClientSite(IOleClientSite *pClientSite)
{
if (_pDispAmbient)
{
_pDispAmbient->Release();
_pDispAmbient = NULL;
}
return SUPERCLASS::SetClientSite(pClientSite);
}
HRESULT CShellOcx::Draw(
DWORD dwDrawAspect,
LONG lindex,
void *pvAspect,
DVTARGETDEVICE *ptd,
HDC hdcTargetDev,
HDC hdcDraw,
LPCRECTL lprcBounds,
LPCRECTL lprcWBounds,
BOOL ( __stdcall *pfnContinue )(ULONG_PTR dwContinue),
ULONG_PTR dwContinue)
{
if (_IsDesignMode())
{
HBRUSH hbrOld = (HBRUSH)SelectObject(hdcDraw, (HBRUSH)GetStockObject(WHITE_BRUSH));
HPEN hpenOld = (HPEN)SelectObject(hdcDraw, (HPEN)GetStockObject(BLACK_PEN));
Rectangle(hdcDraw, lprcBounds->left, lprcBounds->top, lprcBounds->right, lprcBounds->bottom);
MoveToEx(hdcDraw, lprcBounds->left, lprcBounds->top, NULL);
LineTo(hdcDraw, lprcBounds->right, lprcBounds->bottom);
MoveToEx(hdcDraw, lprcBounds->left, lprcBounds->bottom, NULL);
LineTo(hdcDraw, lprcBounds->right, lprcBounds->top);
SelectObject(hdcDraw, hbrOld);
SelectObject(hdcDraw, hpenOld);
return S_OK;
}
return SUPERCLASS::Draw(dwDrawAspect, lindex, pvAspect, ptd, hdcTargetDev, hdcDraw,
lprcBounds, lprcWBounds, pfnContinue, dwContinue);
}
// IPersistStream
HRESULT CShellOcx::GetSizeMax(ULARGE_INTEGER *pcbSize)
{
// REVIEW: this is overly large, I believe E_NOTIMPL is a valid
// return from this and it tells the container that we don't know how big we are.
ULARGE_INTEGER cbMax = { 1028 * 8, 0 }; // isn't this overly large?
*pcbSize = cbMax;
return S_OK;
}
// IOleControl
STDMETHODIMP CShellOcx::GetControlInfo(LPCONTROLINFO pCI)
{
return E_NOTIMPL; // for mnemonics
}
STDMETHODIMP CShellOcx::OnMnemonic(LPMSG pMsg)
{
return E_NOTIMPL; // for mnemonics
}
STDMETHODIMP CShellOcx::OnAmbientPropertyChange(DISPID dispid)
{
switch (dispid)
{
case DISPID_AMBIENT_USERMODE: // design mode vs run mode
case DISPID_UNKNOWN:
_nDesignMode = MODE_UNKNOWN;
break;
}
return S_OK;
}
STDMETHODIMP CShellOcx::FreezeEvents(BOOL bFreeze)
{
_fEventsFrozen = bFreeze;
return S_OK;
}
HRESULT CShellOcx::GetIDsOfNames(REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID *rgdispid)
{
// This is gross, for some reason from VBScript in a page can not get "Document" through so try "Doc" and map
HRESULT hres = CImpIDispatch::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid);
if (FAILED(hres) && (cNames == 1) && rgszNames)
{
OLECHAR const *c_pwszDocument = L"Document";
if (StrCmpIW(*rgszNames, L"Doc") == 0)
hres = CImpIDispatch::GetIDsOfNames(riid, (OLECHAR**)&c_pwszDocument, cNames, lcid, rgdispid);
}
return hres;
}
// ConnectionPointContainer
CConnectionPoint* CShellOcx::_FindCConnectionPointNoRef(BOOL fdisp, REFIID iid)
{
CConnectionPoint* pccp;
if (IsEqualIID(iid, EVENTIIDOFCONTROL(this)) ||
(fdisp && IsEqualIID(iid, IID_IDispatch)))
{
pccp = &m_cpEvents;
}
else if (IsEqualIID(iid, IID_IPropertyNotifySink))
{
pccp = &m_cpPropNotify;
}
else
{
pccp = NULL;
}
return pccp;
}
STDMETHODIMP CShellOcx::EnumConnectionPoints(LPENUMCONNECTIONPOINTS * ppEnum)
{
return CreateInstance_IEnumConnectionPoints(ppEnum, 2,
m_cpEvents.CastToIConnectionPoint(),
m_cpPropNotify.CastToIConnectionPoint());
}
// IProvideClassInfo2
STDMETHODIMP CShellOcx::GetClassInfo(LPTYPEINFO * ppTI)
{
if (!_pClassTypeInfo)
GetTypeInfoFromLibId(LANGIDFROMLCID(g_lcidLocale),
LIBID_SHDocVw, 1, 1, CLSIDOFOBJECT(this), &_pClassTypeInfo);
if (_pClassTypeInfo)
{
_pClassTypeInfo->AddRef();
*ppTI = _pClassTypeInfo;
return S_OK;
}
ppTI = NULL;
return E_FAIL;
}
// IProvideClassInfo2
STDMETHODIMP CShellOcx::GetGUID(DWORD dwGuidKind, GUID *pGUID)
{
if (pGUID == NULL)
return E_POINTER;
if ( (dwGuidKind == GUIDKIND_DEFAULT_SOURCE_DISP_IID)
&& _pObjectInfo->piidEvents)
{
*pGUID = EVENTIIDOFCONTROL(this);
return S_OK;
}
*pGUID = GUID_NULL;
return E_FAIL;
}
// returns TRUE iff MODE_DESIGN
BOOL CShellOcx::_IsDesignMode(void)
{
if (_nDesignMode == MODE_UNKNOWN)
{
VARIANT_BOOL fBool;
if (_GetAmbientProperty(DISPID_AMBIENT_USERMODE, VT_BOOL, &fBool))
{
_nDesignMode = fBool ? MODE_FALSE : MODE_TRUE;
}
else
_nDesignMode = MODE_FALSE;
}
return _nDesignMode == MODE_TRUE;
}
// this table is used for copying data around, and persisting properties.
// basically, it contains the size of a given data type
//
const BYTE g_rgcbDataTypeSize[] = {
0, // VT_EMPTY = 0,
0, // VT_NULL = 1,
sizeof(short), // VT_I2 = 2,
sizeof(long), // VT_I4 = 3,
sizeof(float), // VT_R4 = 4,
sizeof(double), // VT_R8= 5,
sizeof(CURRENCY), // VT_CY= 6,
sizeof(DATE), // VT_DATE = 7,
sizeof(BSTR), // VT_BSTR = 8,
sizeof(IDispatch *), // VT_DISPATCH = 9,
sizeof(SCODE), // VT_ERROR = 10,
sizeof(VARIANT_BOOL), // VT_BOOL = 11,
sizeof(VARIANT), // VT_VARIANT = 12,
sizeof(IUnknown *), // VT_UNKNOWN = 13,
};
// returns the value of an ambient property
//
// Parameters:
// DISPID - [in] property to get
// VARTYPE - [in] type of desired data
// void * - [out] where to put the data
//
// Output:
// BOOL - FALSE means didn't work.
//
// Notes:
//
BOOL CShellOcx::_GetAmbientProperty(DISPID dispid, VARTYPE vt, void *pData)
{
// IE30's WebBrowser OC never requested ambient properties.
// IE40's does and we're finding that apps implemented some of
// the properties we care about incorrectly. Assume old classid
// means this is an old app and fail. The code that calls this
// is smart enough to deal with failure.
//
if (_pObjectInfo->pclsid == &CLSID_WebBrowser_V1)
return FALSE;
HRESULT hr = E_FAIL;
if (!_pDispAmbient && _pcli)
_pcli->QueryInterface(IID_PPV_ARG(IDispatch, &_pDispAmbient));
if (_pDispAmbient)
{
DISPPARAMS dispparams = {0};
VARIANT v;
VariantInit(&v);
hr = _pDispAmbient->Invoke(dispid, IID_NULL, 0, DISPATCH_PROPERTYGET, &dispparams, &v, NULL, NULL);
if (SUCCEEDED(hr))
{
VARIANT vDest;
VariantInit(&vDest);
// we've got the variant, so now go an coerce it to the type
// that the user wants.
//
hr = VariantChangeType(&vDest, &v, 0, vt);
if (SUCCEEDED(hr))
{
// copy the data to where the user wants it
//
CopyMemory(pData, &vDest.lVal, g_rgcbDataTypeSize[vt]);
VariantClear(&vDest);
}
VariantClear(&v);
}
}
return SUCCEEDED(hr);
}