// This is a part of the Microsoft Foundation Classes C++ library. // Copyright (C) 1992-1998 Microsoft Corporation // All rights reserved. // // This source code is only intended as a supplement to the // Microsoft Foundation Classes Reference and related // electronic documentation provided with the library. // See these sources for detailed information regarding the // Microsoft Foundation Classes product. #include "stdafx.h" #ifdef AFX_OLE_SEG #pragma code_seg(AFX_OLE_SEG) #endif #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #define new DEBUG_NEW ///////////////////////////////////////////////////////////////////////////// // Debug helpers #ifdef _DEBUG // Helper for converting IID into useful string. Only for debugging. LPCTSTR AFXAPI AfxGetIIDString(REFIID iid) { static TCHAR szUnfamiliar[80]; TCHAR szByte[3]; struct IID_ENTRY { const IID* piid; LPCTSTR lpszName; }; #define MAKE_IID_ENTRY(name) { &name, _T(#name) } static const IID_ENTRY iidNameTable[] = { MAKE_IID_ENTRY(IID_IAdviseSink), MAKE_IID_ENTRY(IID_IAdviseSink2), MAKE_IID_ENTRY(IID_IBindCtx), MAKE_IID_ENTRY(IID_IClassFactory), MAKE_IID_ENTRY(IID_IContinueCallback), MAKE_IID_ENTRY(IID_IEnumOleDocumentViews), MAKE_IID_ENTRY(IID_IOleCommandTarget), MAKE_IID_ENTRY(IID_IOleDocument), MAKE_IID_ENTRY(IID_IOleDocumentSite), MAKE_IID_ENTRY(IID_IOleDocumentView), MAKE_IID_ENTRY(IID_IPrint), MAKE_IID_ENTRY(IID_IDataAdviseHolder), MAKE_IID_ENTRY(IID_IDataObject), MAKE_IID_ENTRY(IID_IDebug), MAKE_IID_ENTRY(IID_IDebugStream), MAKE_IID_ENTRY(IID_IDfReserved1), MAKE_IID_ENTRY(IID_IDfReserved2), MAKE_IID_ENTRY(IID_IDfReserved3), MAKE_IID_ENTRY(IID_IDispatch), MAKE_IID_ENTRY(IID_IDropSource), MAKE_IID_ENTRY(IID_IDropTarget), MAKE_IID_ENTRY(IID_IEnumCallback), MAKE_IID_ENTRY(IID_IEnumFORMATETC), MAKE_IID_ENTRY(IID_IEnumGeneric), MAKE_IID_ENTRY(IID_IEnumHolder), MAKE_IID_ENTRY(IID_IEnumMoniker), MAKE_IID_ENTRY(IID_IEnumOLEVERB), MAKE_IID_ENTRY(IID_IEnumSTATDATA), MAKE_IID_ENTRY(IID_IEnumSTATSTG), MAKE_IID_ENTRY(IID_IEnumString), MAKE_IID_ENTRY(IID_IEnumUnknown), MAKE_IID_ENTRY(IID_IEnumVARIANT), // MAKE_IID_ENTRY(IID_IExternalConnection), MAKE_IID_ENTRY(IID_IInternalMoniker), MAKE_IID_ENTRY(IID_ILockBytes), MAKE_IID_ENTRY(IID_IMalloc), MAKE_IID_ENTRY(IID_IMarshal), MAKE_IID_ENTRY(IID_IMessageFilter), MAKE_IID_ENTRY(IID_IMoniker), MAKE_IID_ENTRY(IID_IOleAdviseHolder), MAKE_IID_ENTRY(IID_IOleCache), MAKE_IID_ENTRY(IID_IOleCache2), MAKE_IID_ENTRY(IID_IOleCacheControl), MAKE_IID_ENTRY(IID_IOleClientSite), MAKE_IID_ENTRY(IID_IOleContainer), MAKE_IID_ENTRY(IID_IOleInPlaceActiveObject), MAKE_IID_ENTRY(IID_IOleInPlaceFrame), MAKE_IID_ENTRY(IID_IOleInPlaceObject), MAKE_IID_ENTRY(IID_IOleInPlaceSite), MAKE_IID_ENTRY(IID_IOleInPlaceUIWindow), MAKE_IID_ENTRY(IID_IOleItemContainer), MAKE_IID_ENTRY(IID_IOleLink), MAKE_IID_ENTRY(IID_IOleManager), MAKE_IID_ENTRY(IID_IOleObject), MAKE_IID_ENTRY(IID_IOlePresObj), MAKE_IID_ENTRY(IID_IOleWindow), MAKE_IID_ENTRY(IID_IPSFactory), MAKE_IID_ENTRY(IID_IParseDisplayName), MAKE_IID_ENTRY(IID_IPersist), MAKE_IID_ENTRY(IID_IPersistFile), MAKE_IID_ENTRY(IID_IPersistStorage), MAKE_IID_ENTRY(IID_IPersistStream), MAKE_IID_ENTRY(IID_IProxyManager), MAKE_IID_ENTRY(IID_IRootStorage), MAKE_IID_ENTRY(IID_IRpcChannel), MAKE_IID_ENTRY(IID_IRpcProxy), MAKE_IID_ENTRY(IID_IRpcStub), MAKE_IID_ENTRY(IID_IRunnableObject), MAKE_IID_ENTRY(IID_IRunningObjectTable), MAKE_IID_ENTRY(IID_IStdMarshalInfo), MAKE_IID_ENTRY(IID_IStorage), MAKE_IID_ENTRY(IID_IStream), MAKE_IID_ENTRY(IID_IStubManager), MAKE_IID_ENTRY(IID_IUnknown), MAKE_IID_ENTRY(IID_IViewObject), MAKE_IID_ENTRY(IID_IViewObject2), MAKE_IID_ENTRY(IID_NULL), }; #undef MAKE_IID_ENTRY // look for it in the table for (int i = 0; i < _countof(iidNameTable); i++) { if (iid == *iidNameTable[i].piid) return iidNameTable[i].lpszName; } // if we get here, it is some IID_ we haven't heard of... wsprintf(szUnfamiliar, _T("%8.8X-%4.4X-%4.4X-"), iid.Data1, iid.Data2, iid.Data3); for (int nIndex = 0; nIndex < 8; nIndex++) { wsprintf(szByte, _T("%2.2X"), iid.Data4[nIndex]); lstrcat(szUnfamiliar, szByte); } return szUnfamiliar; } #endif ///////////////////////////////////////////////////////////////////////////// // Component object model helpers ///////////////////////////////////////////////////////////////////////////// // IUnknown client helpers LPUNKNOWN AFXAPI _AfxQueryInterface(LPUNKNOWN lpUnknown, REFIID iid) { ASSERT(lpUnknown != NULL); LPUNKNOWN lpW = NULL; if (lpUnknown->QueryInterface(iid, (LPLP)&lpW) != S_OK) return NULL; return lpW; } DWORD AFXAPI _AfxRelease(LPUNKNOWN* lplpUnknown) { ASSERT(lplpUnknown != NULL); if (*lplpUnknown != NULL) { DWORD dwRef = (*lplpUnknown)->Release(); *lplpUnknown = NULL; return dwRef; } return 0; } #define GetInterfacePtr(pTarget, pEntry) \ ((LPUNKNOWN)((BYTE*)pTarget + pEntry->nOffset)) #define GetAggregatePtr(pTarget, pEntry) \ (*(LPUNKNOWN*)((BYTE*)pTarget + pEntry->nOffset)) ///////////////////////////////////////////////////////////////////////////// // CCmdTarget interface map implementation // support for aggregation class CInnerUnknown : public IUnknown { public: STDMETHOD_(ULONG, AddRef)(); STDMETHOD_(ULONG, Release)(); STDMETHOD(QueryInterface)(REFIID iid, LPVOID* ppvObj); }; // calling this function enables an object to be aggregatable void CCmdTarget::EnableAggregation() { // construct an CInnerUnknown just to get to the vtable CInnerUnknown innerUnknown; // copy the vtable & make sure initialized ASSERT(sizeof(m_xInnerUnknown) == sizeof(CInnerUnknown)); m_xInnerUnknown = *(DWORD*)&innerUnknown; } DWORD CCmdTarget::ExternalAddRef() { // delegate to controlling unknown if aggregated if (m_pOuterUnknown != NULL) return m_pOuterUnknown->AddRef(); return InternalAddRef(); } DWORD CCmdTarget::InternalRelease() { ASSERT(GetInterfaceMap() != NULL); if (m_dwRef == 0) return 0; LONG lResult = InterlockedDecrement(&m_dwRef); if (lResult == 0) { AFX_MANAGE_STATE(m_pModuleState); OnFinalRelease(); } return lResult; } DWORD CCmdTarget::ExternalRelease() { // delegate to controlling unknown if aggregated if (m_pOuterUnknown != NULL) return m_pOuterUnknown->Release(); return InternalRelease(); } // special QueryInterface used in implementation (does not AddRef) LPUNKNOWN CCmdTarget::GetInterface(const void* iid) { // allow general hook first chance LPUNKNOWN lpUnk; if ((lpUnk = GetInterfaceHook(iid)) != NULL) return lpUnk; const AFX_INTERFACEMAP* pMap = GetInterfaceMap(); ASSERT(pMap != NULL); DWORD lData1 = ((IID*)iid)->Data1; // IUnknown is a special case since nobody really implements *only* it! BOOL bUnknown = ((DWORD*)&IID_IUnknown)[0] == lData1 && ((DWORD*)iid)[1] == ((DWORD*)&IID_IUnknown)[1] && ((DWORD*)iid)[2] == ((DWORD*)&IID_IUnknown)[2] && ((DWORD*)iid)[3] == ((DWORD*)&IID_IUnknown)[3]; if (bUnknown) { do { const AFX_INTERFACEMAP_ENTRY* pEntry = pMap->pEntry; ASSERT(pEntry != NULL); while (pEntry->piid != NULL) { // check INTERFACE_ENTRY macro LPUNKNOWN lpUnk = GetInterfacePtr(this, pEntry); // check vtable pointer (can be NULL) if (*(DWORD*)lpUnk != 0) return lpUnk; // entry did not match -- keep looking ++pEntry; } #ifdef _AFXDLL } while ((pMap = (*pMap->pfnGetBaseMap)()) != NULL); #else } while ((pMap = pMap->pBaseMap) != NULL); #endif // interface ID not found, fail the call return NULL; } // otherwise, walk the interface map to find the matching IID do { const AFX_INTERFACEMAP_ENTRY* pEntry = pMap->pEntry; ASSERT(pEntry != NULL); while (pEntry->piid != NULL) { if (((DWORD*)pEntry->piid)[0] == lData1 && ((DWORD*)pEntry->piid)[1] == ((DWORD*)iid)[1] && ((DWORD*)pEntry->piid)[2] == ((DWORD*)iid)[2] && ((DWORD*)pEntry->piid)[3] == ((DWORD*)iid)[3]) { // check INTERFACE_ENTRY macro LPUNKNOWN lpUnk = GetInterfacePtr(this, pEntry); // check vtable pointer (can be NULL) if (*(DWORD*)lpUnk != 0) return lpUnk; } // entry did not match -- keep looking ++pEntry; } #ifdef _AFXDLL } while ((pMap = (*pMap->pfnGetBaseMap)()) != NULL); #else } while ((pMap = pMap->pBaseMap) != NULL); #endif // interface ID not found, fail the call return NULL; } LPUNKNOWN CCmdTarget::QueryAggregates(const void* iid) { const AFX_INTERFACEMAP* pMap = GetInterfaceMap(); ASSERT(pMap != NULL); // walk the Interface map to call aggregates do { const AFX_INTERFACEMAP_ENTRY* pEntry = pMap->pEntry; // skip non-aggregate entries ASSERT(pEntry != NULL); while (pEntry->piid != NULL) ++pEntry; // call QueryInterface for each aggregate entry while (pEntry->nOffset != (size_t)-1) { LPUNKNOWN lpQuery = GetAggregatePtr(this, pEntry); // it is ok to have aggregate but not created yet if (lpQuery != NULL) { LPUNKNOWN lpUnk = NULL; if (lpQuery->QueryInterface(*(IID*)iid, (LPLP)&lpUnk) == S_OK && lpUnk != NULL) { // QueryInterface successful... return lpUnk; } } // entry did not match -- keep looking ++pEntry; } #ifdef _AFXDLL } while ((pMap = (*pMap->pfnGetBaseMap)()) != NULL); #else } while ((pMap = pMap->pBaseMap) != NULL); #endif // interface ID not found, fail the call return NULL; } // real implementation of QueryInterface DWORD CCmdTarget::InternalQueryInterface(const void* iid, LPVOID* ppvObj) { // check local interfaces if ((*ppvObj = GetInterface(iid)) != NULL) { // interface was found -- add a reference ExternalAddRef(); return S_OK; } // check aggregates if ((*ppvObj = QueryAggregates(iid)) != NULL) return S_OK; // interface ID not found, fail the call return (DWORD)E_NOINTERFACE; } // QueryInterface that is exported to normal clients DWORD CCmdTarget::ExternalQueryInterface(const void* iid, LPVOID* ppvObj) { // delegate to controlling unknown if aggregated if (m_pOuterUnknown != NULL) return m_pOuterUnknown->QueryInterface(*(IID*)iid, ppvObj); return InternalQueryInterface(iid, ppvObj); } ///////////////////////////////////////////////////////////////////////////// // Inner IUnknown implementation (for aggregation) STDMETHODIMP_(ULONG) CInnerUnknown::AddRef() { METHOD_PROLOGUE_(CCmdTarget, InnerUnknown) return pThis->InternalAddRef(); } STDMETHODIMP_(ULONG) CInnerUnknown::Release() { METHOD_PROLOGUE(CCmdTarget, InnerUnknown) return pThis->InternalRelease(); } STDMETHODIMP CInnerUnknown::QueryInterface(REFIID iid, LPVOID* ppvObj) { METHOD_PROLOGUE_(CCmdTarget, InnerUnknown) if (iid == IID_IUnknown) { // QueryInterface on inner IUnknown for IID_IUnknown must // return inner IUnknown. pThis->InternalAddRef(); *ppvObj = this; return S_OK; } return pThis->InternalQueryInterface(&iid, ppvObj); } ///////////////////////////////////////////////////////////////////////////// // other helper functions // ExternalDisconnect is used to remove RPC connections in destructors. This // insures that no RPC calls will go to the object after it has been // deleted. void CCmdTarget::ExternalDisconnect() { if (m_dwRef == 0) // already in disconnected state? return; // get IUnknown pointer for the object LPUNKNOWN lpUnknown = (LPUNKNOWN)GetInterface(&IID_IUnknown); ASSERT(lpUnknown != NULL); // disconnect the object InterlockedIncrement(&m_dwRef); // protect object from destruction CoDisconnectObject(lpUnknown, 0); m_dwRef = 0; // now in disconnected state } // GetControllingUnknown is used when creating aggregate objects, // usually from OnCreateAggregates. The outer, or controlling, unknown // is one of the parameters to CoCreateInstance and other OLE creation // functions which support aggregation. LPUNKNOWN CCmdTarget::GetControllingUnknown() { if (m_pOuterUnknown != NULL) return m_pOuterUnknown; // aggregate of m_pOuterUnknown LPUNKNOWN lpUnknown = (LPUNKNOWN)GetInterface(&IID_IUnknown); return lpUnknown; // return our own IUnknown implementation } ///////////////////////////////////////////////////////////////////////////// // Inline function declarations expanded out-of-line #ifndef _AFX_ENABLE_INLINES // expand inlines for OLE general APIs static char _szAfxOleInl[] = "afxole.inl"; #undef THIS_FILE #define THIS_FILE _szAfxOleInl #define _AFXDISP_INLINE #include "afxole.inl" #endif //!_AFX_ENABLE_INLINES /////////////////////////////////////////////////////////////////////////////