// Copyright (C) 1996-1997 Microsoft Corporation. All rights reserved. #include "header.h" #include "CtlHelp.H" #include "StdEnum.H" #include "internet.h" #include "hhctrl.h" #include #include #ifndef _DEBUG #undef THIS_FILE static const char THIS_FILE[] = __FILE__; #endif // this is used in our window proc so that we can find out who was last created static COleControl *s_pLastControlCreated; //=--------------------------------------------------------------------------= // COleControl::COleControl //=--------------------------------------------------------------------------= // constructor // // Parameters: // IUnknown * - [in] controlling Unknown // int - [in] type of primary dispatch interface OBJECT_TYPE_* // void * - [in] pointer to entire object // // Notes: // COleControl::COleControl ( IUnknown *pUnkOuter, int iPrimaryDispatch, void *pMainInterface ) : CAutomationObject(pUnkOuter, iPrimaryDispatch, pMainInterface), m_cpEvents(SINK_TYPE_EVENT), m_cpPropNotify(SINK_TYPE_PROPNOTIFY) { // initialize all our variables -- we decided against using a memory-zeroing // memory allocator, so we sort of have to do this work now ... // m_nFreezeEvents = 0; m_pClientSite = NULL; m_pControlSite = NULL; m_pInPlaceSite = NULL; m_pInPlaceFrame = NULL; m_pInPlaceUIWindow = NULL; m_pInPlaceSiteWndless = NULL; // certain hosts don't like 0,0 as your initial size, so we're going to set // our initial size to 100,50 [so it's at least sort of visible on the screen] m_Size.cx = 100; m_Size.cy = 50; SetRectEmpty(&m_rcLocation); m_hwnd = NULL; m_hwndParent = NULL; m_nFreezeEvents = 0; // m_pSimpleFrameSite = NULL; m_pOleAdviseHolder = NULL; m_pViewAdviseSink = NULL; m_pDispAmbient = NULL; m_fDirty = FALSE; m_fModeFlagValid = FALSE; m_fInPlaceActive = FALSE; m_fInPlaceVisible = FALSE; m_fUIActive = FALSE; m_fSaveSucceeded = FALSE; m_fViewAdvisePrimeFirst = FALSE; m_fViewAdviseOnlyOnce = FALSE; m_fRunMode = FALSE; } //=--------------------------------------------------------------------------= // COleControl::~COleControl //=--------------------------------------------------------------------------= // "We are all of us resigned to death; it's life we aren't resigned to." // - Graham Greene (1904-91) // // Notes: // COleControl::~COleControl() { // if we've still got a window, go and kill it now. // if (m_hwnd) { // so our window proc doesn't crash. // SetWindowLong(m_hwnd, GWLP_USERDATA, 0xFFFFFFFF); DestroyWindow(m_hwnd); } // clean up all the pointers we're holding around. QUICK_RELEASE(m_pClientSite); QUICK_RELEASE(m_pControlSite); QUICK_RELEASE(m_pInPlaceSite); QUICK_RELEASE(m_pInPlaceFrame); QUICK_RELEASE(m_pInPlaceUIWindow); // QUICK_RELEASE(m_pSimpleFrameSite); QUICK_RELEASE(m_pOleAdviseHolder); QUICK_RELEASE(m_pViewAdviseSink); QUICK_RELEASE(m_pDispAmbient); QUICK_RELEASE(m_pInPlaceSiteWndless); } //=--------------------------------------------------------------------------= // COleControl::InternalQueryInterface //=--------------------------------------------------------------------------= // derived-controls should delegate back to this when they decide to support // additional interfaces // // Parameters: // REFIID - [in] interface they want // void ** - [out] where they want to put the resulting object ptr. // // Output: // HRESULT - S_OK, E_NOINTERFACE // // Notes: // - NOTE: this function is speed critical!!!! // Using the typedef'd enum lets use see what the value of riid.Data1 is typedef enum { tgIUnknown = 0x00000000, tgIClassFactory = 0x00000001, tgIMalloc = 0x00000002, tgIMarshal = 0x00000003, tgIPSFactory = 0x00000009, tgILockBytes = 0x0000000a, tgIStorage = 0x0000000b, tgIStream = 0x0000000c, tgIEnumSTATSTG = 0x0000000d, tgIBindCtx = 0x0000000e, tgIMoniker = 0x0000000f, tgIRunningObjectTable = 0x00000010, tgIInternalMoniker = 0x00000011, tgIRootStorage = 0x00000012, tgIMessageFilter = 0x00000016, tgIStdMarshalInfo = 0x00000018, tgIExternalConnection = 0x00000019, tgIWeakRef = 0x0000001a, tgIEnumUnknown = 0x00000100, tgIEnumString = 0x00000101, tgIEnumMoniker = 0x00000102, tgIEnumFORMATETC = 0x00000103, tgIEnumOLEVERB = 0x00000104, tgIEnumSTATDATA = 0x00000105, tgIEnumGeneric = 0x00000106, tgIEnumHolder = 0x00000107, tgIEnumCallback = 0x00000108, tgIPersistStream = 0x00000109, tgIPersistStorage = 0x0000010a, tgIPersistFile = 0x0000010b, tgIPersist = 0x0000010c, tgIViewObject = 0x0000010d, tgIDataObject = 0x0000010e, tgIAdviseSink = 0x0000010f, tgIDataAdviseHolder = 0x00000110, tgIOleAdviseHolder = 0x00000111, tgIOleObject = 0x00000112, tgIOleInPlaceObject = 0x00000113, tgIOleWindow = 0x00000114, tgIOleInPlaceUIWindow = 0x00000115, tgIOleInPlaceFrame = 0x00000116, tgIOleInPlaceActiveObject = 0x00000117, tgIOleClientSite = 0x00000118, tgIOleInPlaceSite = 0x00000119, tgIParseDisplayName = 0x0000011a, tgIOleContainer = 0x0000011b, tgIOleItemContainer = 0x0000011c, tgIOleLink = 0x0000011d, tgIOleCache = 0x0000011e, tgIOleManager = 0x0000011f, tgIOlePresObj = 0x00000120, tgIDropSource = 0x00000121, tgIDropTarget = 0x00000122, tgIAdviseSink2 = 0x00000125, tgIRunnableObject = 0x00000126, tgIViewObject2 = 0x00000127, tgIOleCache2 = 0x00000128, tgIOleCacheControl = 0x00000129, tgIDispatch = 0x00020400, tgITypeInfo = 0x00020401, tgITypeLib = 0x00020402, tgITypeComp = 0x00020403, tgIEnumVARIANT = 0x00020404, tgICreateTypeInfo = 0x00020405, tgICreateTypeLib = 0x00020406, tgIPropertyPage2 = 0x01e44665, tgIOleInPlaceObjectWindowless = 0x1c2056cc, tgIErrorInfo = 0x1cf2b120, tgICreateErrorInfo = 0x22f03340, tgIPerPropertyBrowsing = 0x376bd3aa, tgIPersistPropertyBag = 0x37D84F60, tgIAdviseSinkEx = 0x3af24290, tgIViewObjectEx = 0x3af24292, tgICategorizeProperties = 0x4d07fc10, tgIPointerInactive = 0x55980ba0, tgIFormExpert = 0x5aac7f70, tgIOleInPlaceComponent = 0x5efc7970, tgIGangConnectWithDefault = 0x6d5140c0, tgIServiceProvider = 0x6d5140c1, tgISelectionContainer = 0x6d5140c6, tgIRequireClasses = 0x6d5140d0, tgIProvideDynamicClassInfo = 0x6d5140d1, tgIDataFrameExpert = 0x73687490, tgISimpleFrameSite = 0x742b0e01, tgIPicture = 0x7bf80980, tgIPictureDisp = 0x7bf80981, tgIPersistStreamInit = 0x7fd52380, tgIOleUndoAction = 0x894ad3b0, tgIOleInPlaceSiteWindowless = 0x922eada0, tgIDataFrame = 0x97F254E0, tgIControl_95 = 0x9a4bbfb5, tgIPropertyNotifySink = 0x9bfbbc02, tgIOleInPlaceSiteEx = 0x9c2cad80, tgIOleCompoundUndoAction = 0xa1faf330, tgIControl = 0xa7fddba0, tgIProvideClassInfo = 0xb196b283, tgIConnectionPointContainer = 0xb196b284, tgIEnumConnectionPoints = 0xb196b285, tgIConnectionPoint = 0xb196b286, tgIEnumConnections = 0xb196b287, tgIOleControl = 0xb196b288, tgIOleControlSite = 0xb196b289, tgISpecifyPropertyPages = 0xb196b28b, tgIPropertyPageSite = 0xb196b28c, tgIPropertyPage = 0xb196b28d, tgIClassFactory2 = 0xb196b28f, tgIEnumOleUndoActions = 0xb3e7c340, tgIMsoDocument = 0xb722bcc5, tgIMsoView = 0xb722bcc6, tgIMsoCommandTarget = 0xb722bccb, tgIOlePropertyFrame = 0xb83bb801, tgIPropertyPageInPlace = 0xb83bb802, tgIPropertyPage3 = 0xb83bb803, tgIPropertyPageSite2 = 0xb83bb804, tgIFont = 0xbef6e002, tgIFontDisp = 0xbef6e003, tgIQuickActivate = 0xcf51ed10, tgIOleUndoActionManager = 0xd001f200, tgIPSFactoryBuffer = 0xd5f569d0, tgIOleStandardTool = 0xd97877c4, tgISupportErrorInfo = 0xdf0b3d60, tgICDataDoc = 0xF413E4C0, } DATA1_GUIDS; HRESULT COleControl::InternalQueryInterface(REFIID riid, void **ppvObjOut) { #ifdef _DEBUG DATA1_GUIDS gd = (DATA1_GUIDS) riid.Data1; #endif switch (riid.Data1) { // private interface for prop page support case Data1_IControlPrv: if (DO_GUIDS_MATCH(riid, IID_IControlPrv)) { *ppvObjOut = (void *)this; ExternalAddRef(); return S_OK; } goto NoInterface; case Data1_IOleControl: if (DO_GUIDS_MATCH(riid, IID_IOleControl)) *ppvObjOut = (void *) (IOleControl*) this; break; case Data1_IPointerInactive: if (DO_GUIDS_MATCH(riid, IID_IPointerInactive)) *ppvObjOut = (void *) (IPointerInactive*) this; break; case Data1_IQuickActivate: if (DO_GUIDS_MATCH(riid, IID_IQuickActivate)) *ppvObjOut = (void *) (IQuickActivate*) this; break; case Data1_IOleObject: if (DO_GUIDS_MATCH(riid, IID_IOleObject)) *ppvObjOut = (void *) (IOleObject*) this; break; QI_INHERITS((IPersistStorage *)this, IPersist); QI_INHERITS(this, IPersistStreamInit); QI_INHERITS(this, IOleInPlaceObject); QI_INHERITS(this, IOleInPlaceObjectWindowless); QI_INHERITS((IOleInPlaceActiveObject *)this, IOleWindow); QI_INHERITS(this, IOleInPlaceActiveObject); QI_INHERITS(this, IViewObject); QI_INHERITS(this, IViewObject2); QI_INHERITS(this, IViewObjectEx); QI_INHERITS(this, IConnectionPointContainer); // QI_INHERITS(this, ISpecifyPropertyPages); QI_INHERITS(this, IPersistStorage); QI_INHERITS(this, IPersistPropertyBag); QI_INHERITS(this, IProvideClassInfo); default: goto NoInterface; } // we like the interface, so addref and return ((IUnknown *)(*ppvObjOut))->AddRef(); return S_OK; NoInterface: // delegate to super-class for automation interfaces, etc ... return CAutomationObject::InternalQueryInterface(riid, ppvObjOut); } //=--------------------------------------------------------------------------= // COleControl::FindConnectionPoint [IConnectionPointContainer] //=--------------------------------------------------------------------------= // given an IID, find a connection point sink for it. // // Parameters: // REFIID - [in] interfaces they want // IConnectionPoint ** - [out] where the cp should go // // Output: // HRESULT STDMETHODIMP COleControl::FindConnectionPoint(REFIID riid, IConnectionPoint **ppConnectionPoint) { CHECK_POINTER(ppConnectionPoint); // we support the event interface, and IDispatch for it, and we also // support IPropertyNotifySink. // if (DO_GUIDS_MATCH(riid, EVENTIIDOFCONTROL(m_ObjectType)) || DO_GUIDS_MATCH(riid, IID_IDispatch)) *ppConnectionPoint = &m_cpEvents; else if (DO_GUIDS_MATCH(riid, IID_IPropertyNotifySink)) *ppConnectionPoint = &m_cpPropNotify; else return E_NOINTERFACE; // generic post-processing. // (*ppConnectionPoint)->AddRef(); return S_OK; } //=--------------------------------------------------------------------------= // COleControl::EnumConnectionPoints [IConnectionPointContainer] //=--------------------------------------------------------------------------= // creates an enumerator for connection points. // // Parameters: // IEnumConnectionPoints ** - [out] // // Output: // HRESULT STDMETHODIMP COleControl::EnumConnectionPoints(IEnumConnectionPoints **ppEnumConnectionPoints) { IConnectionPoint **rgConnectionPoints; CHECK_POINTER(ppEnumConnectionPoints); // Alloc an array of connection points [since our standard enum // assumes this and free's it later ] rgConnectionPoints = (IConnectionPoint **) lcCalloc(sizeof(IConnectionPoint *) * 2); RETURN_ON_NULLALLOC(rgConnectionPoints); // we support the event interface for this dude as well as IPropertyNotifySink // rgConnectionPoints[0] = &m_cpEvents; rgConnectionPoints[1] = &m_cpPropNotify; *ppEnumConnectionPoints = (IEnumConnectionPoints *)(IEnumGeneric *) new CStandardEnum(IID_IEnumConnectionPoints, 2, sizeof(IConnectionPoint *), (void *)rgConnectionPoints, CopyAndAddRefObject); if (!*ppEnumConnectionPoints) { lcFree(rgConnectionPoints); return E_OUTOFMEMORY; } return S_OK; } //=--------------------------------------------------------------------------= // COleControl::CConnectionPoint::m_pOleControl //=--------------------------------------------------------------------------= // returns a pointer to the control in which we are nested. // // Output: // COleControl * // // Notes: // inline COleControl *COleControl::CConnectionPoint::m_pOleControl ( void ) { return (COleControl *)((BYTE *)this - ((m_bType == SINK_TYPE_EVENT) ? offsetof(COleControl, m_cpEvents) : offsetof(COleControl, m_cpPropNotify))); } //=--------------------------------------------------------------------------= // COleControl::CConnectionPoint::QueryInterface //=--------------------------------------------------------------------------= // standard qi // // Parameters: // REFIID - [in] interface they want // void ** - [out] where they want to put the resulting object ptr. // // Output: // HRESULT - S_OK, E_NOINTERFACE STDMETHODIMP COleControl::CConnectionPoint::QueryInterface ( REFIID riid, void **ppvObjOut ) { if (DO_GUIDS_MATCH(riid, IID_IConnectionPoint) || DO_GUIDS_MATCH(riid, IID_IUnknown)) { *ppvObjOut = (IConnectionPoint *)this; AddRef(); return S_OK; } return E_NOINTERFACE; } ULONG COleControl::CConnectionPoint::AddRef(void) { return m_pOleControl()->ExternalAddRef(); } ULONG COleControl::CConnectionPoint::Release(void) { return m_pOleControl()->ExternalRelease(); } //=--------------------------------------------------------------------------= // COleControl::CConnectionPoint::GetConnectionInterface //=--------------------------------------------------------------------------= // returns the interface we support connections on. // // Parameters: // IID * - [out] interface we support. // // Output: // HRESULT STDMETHODIMP COleControl::CConnectionPoint::GetConnectionInterface(IID *piid) { if (m_bType == SINK_TYPE_EVENT) *piid = EVENTIIDOFCONTROL(m_pOleControl()->m_ObjectType); else *piid = IID_IPropertyNotifySink; return S_OK; } //=--------------------------------------------------------------------------= // COleControl::CConnectionPoint::GetConnectionPointContainer //=--------------------------------------------------------------------------= // returns the connection point container // // Parameters: // IConnectionPointContainer **ppCPC // // Output: // HRESULT STDMETHODIMP COleControl::CConnectionPoint::GetConnectionPointContainer(IConnectionPointContainer **ppCPC) { return m_pOleControl()->ExternalQueryInterface(IID_IConnectionPointContainer, (void **)ppCPC); } //=--------------------------------------------------------------------------= // COleControl::CConnectiontPoint::Advise //=--------------------------------------------------------------------------= // someboyd wants to be advised when something happens. // // Parameters: // IUnknown * - [in] guy who wants to be advised. // DWORD * - [out] cookie // // Output: // HRESULT STDMETHODIMP COleControl::CConnectionPoint::Advise(IUnknown *pUnk, DWORD *pdwCookie) { HRESULT hr; void *pv; CHECK_POINTER(pdwCookie); // first, make sure everybody's got what they thinks they got if (m_bType == SINK_TYPE_EVENT) { /* * CONSIDER: 12.95 -- this theoretically is broken -- if they do * a find connection point on IDispatch, and they just happened to * also support the Event IID, we'd advise on that. this is not * awesome, but will prove entirely acceptable short term. */ hr = pUnk->QueryInterface(EVENTIIDOFCONTROL(m_pOleControl()->m_ObjectType), &pv); if (FAILED(hr)) hr = pUnk->QueryInterface(IID_IDispatch, &pv); } else hr = pUnk->QueryInterface(IID_IPropertyNotifySink, &pv); RETURN_ON_FAILURE(hr); // finally, add the sink. It's now been cast to the correct type and has // been AddRef'd. return AddSink(pv, pdwCookie); } #ifdef _WIN64 IUnknown* COleControl::CConnectionPoint::CookieToSink( DWORD dwCookie ) { for ( short i=0; iRelease(); return S_OK; #else switch (m_cSinks) { case 1: // it's the only sink. make sure the ptrs are the same, and // then free things up if ((DWORD)m_rgSinks != dwCookie) return CONNECT_E_NOCONNECTION; m_rgSinks = NULL; break; case 2: // there are two sinks. go back down to one sink scenario // if ((DWORD)m_rgSinks[0] != dwCookie && (DWORD)m_rgSinks[1] != dwCookie) return CONNECT_E_NOCONNECTION; pUnk = ((DWORD)m_rgSinks[0] == dwCookie) ? m_rgSinks[1] : ((DWORD)m_rgSinks[1] == dwCookie) ? m_rgSinks[0] : NULL; if (!pUnk) return CONNECT_E_NOCONNECTION; lcFree(m_rgSinks); m_rgSinks = (IUnknown **)pUnk; break; default: // there are more than two sinks. just clean up the hole we've // got in our array now. // for (x = 0; x < m_cSinks; x++) { if ((DWORD)m_rgSinks[x] == dwCookie) break; } if (x == m_cSinks) return CONNECT_E_NOCONNECTION; if (x < m_cSinks - 1) memcpy(&(m_rgSinks[x]), &(m_rgSinks[x + 1]), (m_cSinks -1 - x) * sizeof(IUnknown *)); else m_rgSinks[x] = NULL; break; } // we're happy // m_cSinks--; ((IUnknown *)dwCookie)->Release(); return S_OK; #endif } //=--------------------------------------------------------------------------= // COleControl::CConnectionPoint::EnumConnections //=--------------------------------------------------------------------------= // enumerates all current connections // // Paramters: // IEnumConnections ** - [out] new enumerator object // // Output: // HRESULT // // NOtes: // STDMETHODIMP COleControl::CConnectionPoint::EnumConnections ( IEnumConnections **ppEnumOut ) { CONNECTDATA *rgConnectData = NULL; int i; if (m_cSinks) { // allocate some memory big enough to hold all of the sinks. rgConnectData = (CONNECTDATA *)lcCalloc(m_cSinks * sizeof(CONNECTDATA)); RETURN_ON_NULLALLOC(rgConnectData); #ifdef _WIN64 for (i = 0; i < m_cSinks; i++) { rgConnectData[i].pUnk = m_rgSinks64[i].pUnk; rgConnectData[i].dwCookie = m_rgSinks64[i].dwCookie; } #else // fill in the array // if (m_cSinks == 1) { rgConnectData[0].pUnk = (IUnknown *)m_rgSinks; rgConnectData[0].dwCookie = (DWORD)m_rgSinks; } else { // loop through all available sinks. // for (i = 0; i < m_cSinks; i++) { rgConnectData[i].pUnk = m_rgSinks[i]; rgConnectData[i].dwCookie = (DWORD)m_rgSinks[i]; } } #endif } // create yon enumerator object. // *ppEnumOut = (IEnumConnections *)(IEnumGeneric *)new CStandardEnum(IID_IEnumConnections, m_cSinks, sizeof(CONNECTDATA), rgConnectData, CopyAndAddRefObject); if (!*ppEnumOut) { lcFree(rgConnectData); return E_OUTOFMEMORY; } return S_OK; } //=--------------------------------------------------------------------------= // COleControl::CConnectionPoint::~CConnectionPoint //=--------------------------------------------------------------------------= // cleans up // // Notes: // COleControl::CConnectionPoint::~CConnectionPoint () { int x; #ifdef _WIN64 if (!m_cSinks) return; else { for (x = 0; x < m_cSinks; x++) QUICK_RELEASE(m_rgSinks64[x].pUnk); lcFree(m_rgSinks64); } #else // clean up some memory stuff // if (!m_cSinks) return; else if (m_cSinks == 1) ((IUnknown *)m_rgSinks)->Release(); else { for (x = 0; x < m_cSinks; x++) QUICK_RELEASE(m_rgSinks[x]); lcFree(m_rgSinks); } #endif } //=--------------------------------------------------------------------------= // COleControl::CConnectionPiont::DoInvoke //=--------------------------------------------------------------------------= // fires an event to all listening on our event interface. // // Parameters: // DISPID - [in] event to fire. // DISPPARAMS - [in] void COleControl::CConnectionPoint::DoInvoke(DISPID dispid, DISPPARAMS *pdispparams) { int iConnection; // if we don't have any sinks, then there's nothing to do. we intentionally // ignore errors here. #ifdef _WIN64 if (m_cSinks == 0) return; else for (iConnection = 0; iConnection < m_cSinks; iConnection++) ((IDispatch *)m_rgSinks64[iConnection].pUnk)->Invoke(dispid, IID_NULL, 0, DISPATCH_METHOD, pdispparams, NULL, NULL, NULL); #else if (m_cSinks == 0) return; else if (m_cSinks == 1) ((IDispatch *)m_rgSinks)->Invoke(dispid, IID_NULL, 0, DISPATCH_METHOD, pdispparams, NULL, NULL, NULL); else for (iConnection = 0; iConnection < m_cSinks; iConnection++) ((IDispatch *)m_rgSinks[iConnection])->Invoke(dispid, IID_NULL, 0, DISPATCH_METHOD, pdispparams, NULL, NULL, NULL); #endif } //=--------------------------------------------------------------------------= // COleControl::CConnectionPoint::DoOnChanged //=--------------------------------------------------------------------------= // fires the OnChanged event for IPropertyNotifySink listeners. // // Parameters: // DISPID - [in] dude that changed. // // Output: // none void COleControl::CConnectionPoint::DoOnChanged(DISPID dispid) { ASSERT_COMMENT(FALSE, "DoOnChanged called, restore the code"); #if 0 int iConnection; // if we don't have any sinks, then there's nothing to do. if (m_cSinks == 0) return; else if (m_cSinks == 1) ((IPropertyNotifySink *)m_rgSinks)->OnChanged(dispid); else for (iConnection = 0; iConnection < m_cSinks; iConnection++) ((IPropertyNotifySink *)m_rgSinks[iConnection])->OnChanged(dispid); #endif } //=--------------------------------------------------------------------------= // COleControl::CConnectionPoint::DoOnRequestEdit //=--------------------------------------------------------------------------= // fires the OnRequestEdit for IPropertyNotifySinkListeners // // Parameters: // DISPID - [in] dispid user wants to change. // // Output: // BOOL - false means you cant BOOL COleControl::CConnectionPoint::DoOnRequestEdit(DISPID dispid) { HRESULT hr; int iConnection; #ifdef _WIN64 if (m_cSinks == 0) hr = S_OK; else { for (iConnection = 0; iConnection < m_cSinks; iConnection++) { hr = ((IPropertyNotifySink *)m_rgSinks64[iConnection].pUnk)->OnRequestEdit(dispid); if (hr != S_OK) break; } } #else // if we don't have any sinks, then there's nothing to do. if (m_cSinks == 0) hr = S_OK; else if (m_cSinks == 1) hr =((IPropertyNotifySink *)m_rgSinks)->OnRequestEdit(dispid); else { for (iConnection = 0; iConnection < m_cSinks; iConnection++) { hr = ((IPropertyNotifySink *)m_rgSinks[iConnection])->OnRequestEdit(dispid); if (hr != S_OK) break; } } #endif return (hr == S_OK) ? TRUE : FALSE; } //=--------------------------------------------------------------------------= // COleControl::CreateInPlaceWindow //=--------------------------------------------------------------------------= // creates the window with which we will be working. // yay. // // Parameters: // int - [in] left // int - [in] top // BOOL - [in] can we skip redrawing? // // Output: // HWND // // Notes: // - DANGER! DANGER! this function is protected so that anybody can call it // from their control. however, people should be extremely careful of when // and why they do this. preferably, this function would only need to be // called by an end-control writer in design mode to take care of some // hosting/painting issues. otherwise, the framework should be left to // call it when it wants. HWND COleControl::CreateInPlaceWindow(int x, int y, BOOL fNoRedraw) { BOOL fVisible; DWORD dwWindowStyle, dwExWindowStyle; char szWindowTitle[128]; // if we've already got a window, do nothing. if (m_hwnd) return m_hwnd; // get the user to register the class if it's not already // been done. we have to critical section this since more than one thread // can be trying to create this control if (!CTLWNDCLASSREGISTERED(m_ObjectType)) { // EnterCriticalSection(&g_CriticalSection); if (!RegisterClassData()) { // LeaveCriticalSection(&g_CriticalSection); return NULL; } else CTLWNDCLASSREGISTERED(m_ObjectType) = TRUE; // LeaveCriticalSection(&g_CriticalSection); } dwWindowStyle = dwExWindowStyle = 0; szWindowTitle[0] = '\0'; if (!BeforeCreateWindow(&dwWindowStyle, &dwExWindowStyle, szWindowTitle)) return NULL; dwWindowStyle |= (WS_CHILD | WS_CLIPSIBLINGS); // create window visible if parent hidden (common case) // otherwise, create hidden, then shown. this is a little subtle, but // it makes sense eventually. ASSERT(m_hwndParent); // why would this ever occur? [rw] if (!m_hwndParent) m_hwndParent = GetParkingWindow(); fVisible = IsWindowVisible(m_hwndParent); if (!fVisible) dwWindowStyle |= WS_VISIBLE; // we have to mutex the entire create window process since we need to use // the s_pLastControlCreated to pass in the object pointer. nothing too // serious // EnterCriticalSection(&g_CriticalSection); s_pLastControlCreated = this; m_fCreatingWindow = TRUE; // finally, go create the window, parenting it as appropriate. m_hwnd = CreateWindowEx(dwExWindowStyle, WNDCLASSNAMEOFCONTROL(m_ObjectType), szWindowTitle, dwWindowStyle, x, y, m_Size.cx, m_Size.cy, m_hwndParent, NULL, _Module.GetModuleInstance(), NULL); // clean up some variables, and leave the critical section m_fCreatingWindow = FALSE; s_pLastControlCreated = NULL; // LeaveCriticalSection(&g_CriticalSection); if (IsValidWindow(m_hwnd)) { // let the derived-control do something if they so desire if (!AfterCreateWindow()) { BeforeDestroyWindow(); SetWindowLong(m_hwnd, GWLP_USERDATA, 0xFFFFFFFF); DestroyWindow(m_hwnd); m_hwnd = NULL; return m_hwnd; } // if we didn't create the window visible, show it now. if (fVisible) SetWindowPos(m_hwnd, // m_hwndParent, NULL, // RAID #30314 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_SHOWWINDOW | (fNoRedraw) ? SWP_NOREDRAW : 0); } // finally, tell the host of this if (m_pClientSite) m_pClientSite->ShowObject(); return m_hwnd; } //=--------------------------------------------------------------------------= // COleControl::SetInPlaceParent [helper] //=--------------------------------------------------------------------------= // sets up the parent window for our control. // // Parameters: // HWND - [in] new parent window void COleControl::SetInPlaceParent(HWND hwndParent) { ASSERT_COMMENT(!m_pInPlaceSiteWndless, "This routine should only get called for windowed OLE controls"); if (m_hwndParent == hwndParent) return; m_hwndParent = hwndParent; if (m_hwnd) SetParent(m_hwnd, hwndParent); } //=--------------------------------------------------------------------------= // COleControl::ControlWindowProc //=--------------------------------------------------------------------------= // default window proc for an OLE Control. controls will have their own // window proc called from this one, after some processing is done. // // Parameters: // - see win32sdk docs. // // Notes: // LRESULT CALLBACK COleControl::ControlWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { COleControl *pCtl = ControlFromHwnd(hwnd); LRESULT lResult; // if the value isn't a positive value, then it's in some special // state [creation or destruction] this is safe because under win32, // the upper 2GB of an address space aren't available. if (!pCtl) { pCtl = s_pLastControlCreated; SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) pCtl); pCtl->m_hwnd = hwnd; } // 17-Sep-1997 [ralphw] Changed to 0xFFFFFFFF to avoid 32-bit dependencies else if ((DWORD_PTR) pCtl == (DWORD_PTR) 0xFFFFFFFF) { return DefWindowProc(hwnd, msg, wParam, lParam); } // message preprocessing #if 0 if (pCtl->m_pSimpleFrameSite) { hr = pCtl->m_pSimpleFrameSite->PreMessageFilter(hwnd, msg, wParam, lParam, &lResult, &dwCookie); if (hr == S_FALSE) return lResult; } #endif // for certain messages, do not call the user window proc. instead, // we have something else we'd like to do. switch (msg) { case WM_MOUSEACTIVATE: { pCtl->InPlaceActivate(OLEIVERB_UIACTIVATE); break; } case WM_PAINT: { // call the user's OnDraw routine. PAINTSTRUCT ps; RECT rc; HDC hdc; // if we're given an HDC, then use it // REVIEW: 17-Sep-1997 [ralphw] who's handing us a dc in wParam? if (!wParam) hdc = BeginPaint(hwnd, &ps); else hdc = (HDC)wParam; GetClientRect(hwnd, &rc); pCtl->OnDraw(DVASPECT_CONTENT, hdc, (RECTL *)&rc, NULL, NULL, TRUE); if (!wParam) EndPaint(hwnd, &ps); } break; default: // call the derived-control's window proc lResult = pCtl->WindowProc(msg, wParam, lParam); break; } // message postprocessing switch (msg) { case WM_NCDESTROY: // after this point, the window doesn't exist any more pCtl->m_hwnd = NULL; break; // REVIEW: 14-Oct-1997 [ralphw] This message never arrives case WM_SETFOCUS: case WM_KILLFOCUS: // give the control site focus notification if (pCtl->m_fInPlaceActive && pCtl->m_pControlSite) { DBWIN("Focus change"); pCtl->m_pControlSite->OnFocus(msg == WM_SETFOCUS); } break; case WM_SIZE: // a change in size is a change in view if (!pCtl->m_fCreatingWindow) pCtl->ViewChanged(); break; } // lastly, simple frame postmessage processing #if 0 if (pCtl->m_pSimpleFrameSite) pCtl->m_pSimpleFrameSite->PostMessageFilter(hwnd, msg, wParam, lParam, &lResult, dwCookie); #endif return lResult; } //=--------------------------------------------------------------------------= // COleControl::SetFocus //=--------------------------------------------------------------------------= // we have to override this routine to get UI Activation correct. // // Parameters: // BOOL - [in] true means take, false release // // Output: // BOOL // // Notes: // - CONSIDER: this is pretty messy, and it's still not entirely clear // what the ole control/focus story is. // REVIEW: 14-Oct-1997 [ralphw] Doesn't look like it is ever called BOOL COleControl::SetFocus(BOOL fGrab) { DBWIN("COleControl::SetFocus"); HRESULT hr; HWND hwnd; // first thing to do is check out UI Activation state, and then set // focus [either with windows api, or via the host for windowless // controls] if (m_pInPlaceSiteWndless) { if (!m_fUIActive && fGrab) if (FAILED(InPlaceActivate(OLEIVERB_UIACTIVATE))) return FALSE; hr = m_pInPlaceSiteWndless->SetFocus(fGrab); return (hr == S_OK) ? TRUE : FALSE; } else { // we've got a window. if (m_fInPlaceActive) { hwnd = (fGrab) ? m_hwnd : m_hwndParent; if (!m_fUIActive && fGrab) return SUCCEEDED(InPlaceActivate(OLEIVERB_UIACTIVATE)); else return (::SetFocus(hwnd) == hwnd); } else return FALSE; } // dead code } #if 0 //=--------------------------------------------------------------------------= // COleControl::ReflectWindowProc //=--------------------------------------------------------------------------= // reflects window messages on to the child window. // // Parameters and Output: // - see win32 sdk docs // REVIEW: 14-Oct-1997 [ralphw] Doesn't seem to ever be called LRESULT CALLBACK COleControl::ReflectWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { COleControl *pCtl; switch (msg) { case WM_COMMAND: case WM_NOTIFY: case WM_CTLCOLORBTN: case WM_CTLCOLORDLG: case WM_CTLCOLOREDIT: case WM_CTLCOLORLISTBOX: case WM_CTLCOLORMSGBOX: case WM_CTLCOLORSCROLLBAR: case WM_CTLCOLORSTATIC: case WM_DRAWITEM: case WM_MEASUREITEM: case WM_DELETEITEM: case WM_VKEYTOITEM: case WM_CHARTOITEM: case WM_COMPAREITEM: case WM_HSCROLL: case WM_VSCROLL: case WM_PARENTNOTIFY: case WM_SETFOCUS: case WM_SIZE: pCtl = (COleControl *) GetWindowLong(hwnd, GWLP_USERDATA); if (pCtl) return SendMessage(pCtl->m_hwnd, msg, wParam, lParam); break; } return DefWindowProc(hwnd, msg, wParam, lParam); } #endif //=--------------------------------------------------------------------------= // COleControl::GetAmbientProperty [callable] //=--------------------------------------------------------------------------= // 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. BOOL COleControl::GetAmbientProperty(DISPID dispid, VARTYPE vt, void *pData) { DISPPARAMS dispparams; VARIANT v, v2; HRESULT hr; v.vt = VT_EMPTY; v.lVal = 0; v2.vt = VT_EMPTY; v2.lVal = 0; // get a pointer to the source of ambient properties. // if (!m_pDispAmbient) { if (m_pClientSite) m_pClientSite->QueryInterface(IID_IDispatch, (void **)&m_pDispAmbient); if (!m_pDispAmbient) return FALSE; } // now go and get the property into a variant. memset(&dispparams, 0, sizeof(DISPPARAMS)); hr = m_pDispAmbient->Invoke(dispid, IID_NULL, 0, DISPATCH_PROPERTYGET, &dispparams, &v, NULL, NULL); if (FAILED(hr)) return FALSE; // we've got the variant, so now go an coerce it to the type that the user // wants. if the types are the same, then this will copy the stuff to // do appropriate ref counting ... // hr = VariantChangeType(&v2, &v, 0, vt); if (FAILED(hr)) { VariantClear(&v); return FALSE; } // copy the data to where the user wants it CopyMemory(pData, &(v2.lVal), g_rgcbDataTypeSize[vt]); VariantClear(&v); return TRUE; } //=--------------------------------------------------------------------------= // COleControl::GetAmbientFont [callable] //=--------------------------------------------------------------------------= // gets the current font for the user. // // Parameters: // IFont ** - [out] where to put the font. // // Output: // BOOL - FALSE means couldn't get it. BOOL COleControl::GetAmbientFont(IFont **ppFont) { IDispatch *pFontDisp; // we don't have to do much here except get the ambient property and QI // it for the user. *ppFont = NULL; if (!GetAmbientProperty(DISPID_AMBIENT_FONT, VT_DISPATCH, &pFontDisp)) return FALSE; pFontDisp->QueryInterface(IID_IFont, (void **)ppFont); pFontDisp->Release(); return (*ppFont) ? TRUE : FALSE; } //=--------------------------------------------------------------------------= // COleControl::DesignMode //=--------------------------------------------------------------------------= // returns TRUE if we're in Design mode. // // Output: // BOOL - true is design mode, false is run mode // // Notes: // BOOL COleControl::DesignMode(void) { // BUGBUG: if we never call this, then remove it VARIANT_BOOL f; // if we don't already know our run mode, go and get it. we'll assume // it's true unless told otherwise [or if the operation fails ...] if (!m_fModeFlagValid) { f = TRUE; m_fModeFlagValid = TRUE; GetAmbientProperty(DISPID_AMBIENT_USERMODE, VT_BOOL, &f); m_fRunMode = f; } return !m_fRunMode; } //=--------------------------------------------------------------------------= // COleControl::FireEvent //=--------------------------------------------------------------------------= // fires an event. handles arbitrary number of arguments. // // Parameters: // EVENTINFO * - [in] struct that describes the event. // ... - arguments to the event // // Output: // none // // Notes: // - use stdarg's va_* macros. void __cdecl COleControl::FireEvent(EVENTINFO *pEventInfo, ...) { va_list valist; DISPPARAMS dispparams; VARIANT rgvParameters[MAX_ARGS]; VARIANT *pv; VARTYPE vt; int iParameter; int cbSize; ASSERT_COMMENT(pEventInfo->cParameters <= MAX_ARGS, "Doesn't support more than MAX_ARGS params."); va_start(valist, pEventInfo); // copy the Parameters into the rgvParameters array. make sure we reverse // them for automation // pv = &(rgvParameters[pEventInfo->cParameters - 1]); for (iParameter = 0; iParameter < pEventInfo->cParameters; iParameter++) { vt = pEventInfo->rgTypes[iParameter]; // if it's a by value variant, then just copy the whole thing if (vt == VT_VARIANT) *pv = va_arg(valist, VARIANT); else { // copy the vt and the data value. pv->vt = vt; if (vt & VT_BYREF) cbSize = sizeof(void *); else cbSize = g_rgcbDataTypeSize[vt]; // small optimization -- we can copy 2/4 bytes over quite // quickly. if (cbSize == sizeof(short)) V_I2(pv) = va_arg(valist, short); else if (cbSize == 4) V_I4(pv) = va_arg(valist, long); else { // copy over 8 bytes ASSERT_COMMENT(cbSize == 8, "don't recognize the type!!"); V_CY(pv) = va_arg(valist, CURRENCY); } } pv--; } // fire the event dispparams.rgvarg = rgvParameters; dispparams.cArgs = pEventInfo->cParameters; dispparams.rgdispidNamedArgs = NULL; dispparams.cNamedArgs = 0; m_cpEvents.DoInvoke(pEventInfo->dispid, &dispparams); va_end(valist); } #if 0 //=--------------------------------------------------------------------------= // COleControl::AfterCreateWindow [overridable] //=--------------------------------------------------------------------------= // // Output: // BOOL - false means fatal error, can't continue BOOL COleControl::AfterCreateWindow(void) { return TRUE; } #endif //=--------------------------------------------------------------------------= // COleControl::InvalidateControl [callable] //=--------------------------------------------------------------------------= void COleControl::InvalidateControl(LPCRECT lpRect) { if (m_fInPlaceActive) InvalidateRect(m_hwnd, lpRect, TRUE); else ViewChanged(); } //=--------------------------------------------------------------------------= // COleControl::SetControlSize [callable] //=--------------------------------------------------------------------------= // sets the control size. they'll give us the size in pixels. we've got to // convert them back to HIMETRIC before passing them on! // // Parameters: // SIZEL * - [in] new size // // Output: // BOOL // // Notes: // BOOL COleControl::SetControlSize(SIZEL *pSize) { HRESULT hr; SIZEL slHiMetric; PixelToHiMetric(pSize, &slHiMetric); hr = SetExtent(DVASPECT_CONTENT, &slHiMetric); return (FAILED(hr)) ? FALSE : TRUE; } //=--------------------------------------------------------------------------= // COleControl::RecreateControlWindow [callable] //=--------------------------------------------------------------------------= // called by a [subclassed, typically] control to recreate it's control // window. // // Parameters: // none // // Output: // HRESULT // // Notes: // - NOTE: USE ME EXTREMELY SPARINGLY! THIS IS AN EXTREMELY EXPENSIVE // OPERATION! // #if 0 HRESULT COleControl::RecreateControlWindow(void) { HRESULT hr; HWND hwndPrev; DBWIN("RecreateControlWindow called -- it probably shouldn't be."); // we need to correctly preserve the control's position within the // z-order here. if (m_hwnd) hwndPrev = ::GetWindow(m_hwnd, GW_HWNDPREV); // if we're in place active, then we have to deactivate, and reactivate // ourselves with the new window ... if (m_fInPlaceActive) { hr = InPlaceDeactivate(); RETURN_ON_FAILURE(hr); hr = InPlaceActivate((m_fUIActive) ? OLEIVERB_UIACTIVATE : OLEIVERB_INPLACEACTIVATE); RETURN_ON_FAILURE(hr); } else if (m_hwnd) { DestroyWindow(m_hwnd); m_hwnd = NULL; CreateInPlaceWindow(0, 0, FALSE); } // restore z-order position if (m_hwnd) SetWindowPos(m_hwnd, hwndPrev, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); return m_hwnd ? S_OK : E_FAIL; } #endif