// 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); }