// 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 ///////////////////////////////////////////////////////////////////////////// // COleClientItem activation // helper to get client site -- this is called from a number of places LPOLECLIENTSITE COleClientItem::GetClientSite() { ASSERT_VALID(this); LPOLECLIENTSITE lpClientSite = (LPOLECLIENTSITE)GetInterface(&IID_IOleClientSite); ASSERT(lpClientSite != NULL); return lpClientSite; } void COleClientItem::Activate(LONG nVerb, CView* pView, LPMSG lpMsg) { ASSERT_VALID(this); ASSERT(m_lpObject != NULL); if (pView != NULL) ASSERT_VALID(pView); if (lpMsg != NULL) ASSERT(AfxIsValidAddress(lpMsg, sizeof(MSG), FALSE)); // store the container HWND for in place activation then do the verb if (m_pView == NULL) m_pView = pView; _AFX_OLE_STATE* pOleState = _afxOleState; CView* pViewSave = pOleState->m_pActivateView; pOleState->m_pActivateView = NULL; // get item rectangle for in-place players // (that may not support in-place activation) LPCRECT lpPosRect = NULL; CRect rectPos; if (pView != NULL) { ASSERT_VALID(pView); rectPos.SetRectEmpty(); OnGetItemPosition(rectPos); if (!rectPos.IsRectEmpty()) { lpPosRect = &rectPos; pOleState->m_pActivateView = pView; } } // prepare DoVerb parameters and call into the server LPOLECLIENTSITE lpClientSite = GetClientSite(); HWND hWnd = pView->GetSafeHwnd(); SCODE sc = m_lpObject->DoVerb(nVerb, lpMsg, lpClientSite, -1, hWnd, lpPosRect); pOleState->m_pActivateView = pViewSave; // clear out m_pView in case in-place activation only partially worked if (!IsInPlaceActive()) m_pView = NULL; // update available status based on the results of DoVerb // (this is used in the links dialog). m_bLinkUnavail = (BYTE)FAILED(sc); CheckGeneral(sc); } ////////////////////////////////////////////////////////////////////////////// // Create error handling void COleClientItem::CheckGeneral(SCODE sc) // set 'm_scLast' // throw exception if not ok to continue { ASSERT_VALID(this); m_scLast = S_OK; // assume things are ok // then, check for error if (sc != S_OK) { m_scLast = sc; if (!FAILED(sc)) { #ifdef _DEBUG // warn about non-NULL success codes TRACE1("Warning: operation returned scode = %s.\n", AfxGetFullScodeString(m_scLast)); #endif return; } // this error wasn't expected, so throw an exception AfxThrowOleException(sc); } } ///////////////////////////////////////////////////////////////////////////// // COleClientItem clipboard support void COleClientItem::CopyToClipboard(BOOL bIncludeLink) { ASSERT_VALID(this); ASSERT(m_lpObject != NULL); // get clipboard data for this item COleDataSource* pDataSource = OnGetClipboardData(bIncludeLink, NULL, NULL); TRY { // put it on the clipboard pDataSource->SetClipboard(); } CATCH_ALL(e) { delete pDataSource; THROW_LAST(); } END_CATCH_ALL } COleDataSource* COleClientItem::OnGetClipboardData( BOOL bIncludeLink, LPPOINT lpOffset, LPSIZE lpSize) { ASSERT_VALID(this); COleDataSource* pDataSource = new COleDataSource; TRY { GetClipboardData(pDataSource, bIncludeLink, lpOffset, lpSize); } CATCH_ALL(e) { delete pDataSource; THROW_LAST(); } END_CATCH_ALL ASSERT_VALID(pDataSource); return pDataSource; } DROPEFFECT COleClientItem::DoDragDrop(LPCRECT lpItemRect, CPoint ptOffset, BOOL bIncludeLink, DWORD dwEffects, LPCRECT lpRectStartDrag) { ASSERT(AfxIsValidAddress(lpItemRect, sizeof(RECT))); ASSERT_VALID(this); ASSERT(m_lpObject != NULL); DROPEFFECT dropEffect = DROPEFFECT_NONE; COleDataSource *pDataSource = NULL; TRY { // get clipboard data object for the item CSize sizeItem( lpItemRect->right - lpItemRect->left, lpItemRect->bottom - lpItemRect->top); pDataSource = OnGetClipboardData(bIncludeLink, &ptOffset, &sizeItem); // add DROPEFFECT_LINK only if link source is available LPDATAOBJECT lpDataObject = (LPDATAOBJECT) pDataSource->GetInterface(&IID_IDataObject); ASSERT(lpDataObject != NULL); FORMATETC formatEtc; formatEtc.cfFormat = (CLIPFORMAT)_oleData.cfLinkSource; formatEtc.ptd = NULL; formatEtc.dwAspect = DVASPECT_CONTENT; formatEtc.lindex = -1; formatEtc.tymed = (DWORD) -1; if (lpDataObject->QueryGetData(&formatEtc) == S_OK) dwEffects |= DROPEFFECT_LINK; // calculate default sensitivity rectangle CRect rectDrag; if (lpRectStartDrag == NULL) { rectDrag.SetRect(lpItemRect->left, lpItemRect->bottom, lpItemRect->left, lpItemRect->bottom); lpRectStartDrag = &rectDrag; } // do drag drop operation dropEffect = pDataSource->DoDragDrop(dwEffects, lpRectStartDrag); pDataSource->InternalRelease(); } CATCH_ALL(e) { if (pDataSource != NULL) pDataSource->InternalRelease(); THROW_LAST(); } END_CATCH_ALL return dropEffect; } void COleClientItem::GetClipboardData(COleDataSource* pDataSource, BOOL bIncludeLink, LPPOINT lpOffset, LPSIZE lpSize) { ASSERT_VALID(this); ASSERT(m_lpObject != NULL); ASSERT_VALID(pDataSource); // add CF_EMBEDDEDOBJECT by creating memory storage copy of the object STGMEDIUM stgMedium; GetEmbeddedItemData(&stgMedium); pDataSource->CacheData((CLIPFORMAT)_oleData.cfEmbeddedObject, &stgMedium); // add CF_OBJECTDESCRIPTOR GetObjectDescriptorData(lpOffset, lpSize, &stgMedium); pDataSource->CacheData((CLIPFORMAT)_oleData.cfObjectDescriptor, &stgMedium); // add any presentation entries in the object's cache AddCachedData(pDataSource); // add CF_LINKSOURCE if supporting links to embeddings if (bIncludeLink && GetLinkSourceData(&stgMedium)) { pDataSource->CacheData((CLIPFORMAT)_oleData.cfLinkSource, &stgMedium); // add CF_LINKSOURCEDESCRIPTOR GetObjectDescriptorData(lpOffset, lpSize, &stgMedium); pDataSource->CacheData((CLIPFORMAT)_oleData.cfLinkSourceDescriptor, &stgMedium); } } BOOL PASCAL COleClientItem::CanPaste() { // it is faster and more reliable to use the Windows clipboard // APIs instead of OleQueryCreateFromData. return IsClipboardFormatAvailable(_oleData.cfEmbedSource) || IsClipboardFormatAvailable(_oleData.cfEmbeddedObject) || IsClipboardFormatAvailable(_oleData.cfFileName) || IsClipboardFormatAvailable(_oleData.cfFileNameW) || IsClipboardFormatAvailable(CF_METAFILEPICT) || IsClipboardFormatAvailable(CF_DIB) || IsClipboardFormatAvailable(CF_BITMAP) || (IsClipboardFormatAvailable(_oleData.cfOwnerLink) && IsClipboardFormatAvailable(_oleData.cfNative)); } BOOL PASCAL COleClientItem::CanPasteLink() { // it is faster and more reliable to use the Windows clipboard // APIs instead of OleQueryCreateFromData. return IsClipboardFormatAvailable(_oleData.cfLinkSource) || IsClipboardFormatAvailable(_oleData.cfFileName) || IsClipboardFormatAvailable(_oleData.cfFileNameW) || IsClipboardFormatAvailable(_oleData.cfObjectLink); } BOOL PASCAL COleClientItem::CanCreateFromData(const COleDataObject* pDataObject) { if (pDataObject->m_bClipboard) return COleClientItem::CanPaste(); ((COleDataObject*)pDataObject)->EnsureClipboardObject(); if (pDataObject->m_lpDataObject == NULL) return FALSE; SCODE sc = ::OleQueryCreateFromData(pDataObject->m_lpDataObject); return !FAILED(sc) && sc != S_FALSE; } BOOL PASCAL COleClientItem::CanCreateLinkFromData(const COleDataObject* pDataObject) { if (pDataObject->m_bClipboard) return COleClientItem::CanPasteLink(); ((COleDataObject*)pDataObject)->EnsureClipboardObject(); if (pDataObject->m_lpDataObject == NULL) return FALSE; SCODE sc = ::OleQueryLinkFromData(pDataObject->m_lpDataObject); return !FAILED(sc) && sc != S_FALSE; } ///////////////////////////////////////////////////////////////////////////// // Conversion & Activate As support BOOL COleClientItem::ConvertTo(REFCLSID clsidNew) { ASSERT_VALID(this); ASSERT(m_lpObject != NULL); // first, close the object Close(); // convert it m_scLast = _AfxOleDoConvert(m_lpStorage, clsidNew); if (FAILED(m_scLast)) return FALSE; // save IOleObject and IViewObject2 pointers LPOLEOBJECT lpObject = m_lpObject; LPVIEWOBJECT2 lpViewObject = m_lpViewObject; DWORD dwConnection = m_dwConnection; // NULL out IOleObject and IViewObject2 cached pointers m_lpObject = NULL; m_lpViewObject = NULL; m_dwConnection = 0; // then load the new object from the new storage BOOL bResult = FinishCreate(::OleLoad(m_lpStorage, IID_IUnknown, NULL, (LPLP)&m_lpObject)); if (bResult) { RELEASE(lpObject); RELEASE(lpViewObject); } else { m_lpObject = lpObject; m_lpViewObject = lpViewObject; m_dwConnection = dwConnection; UpdateItemType(); } ASSERT_VALID(this); return bResult; } BOOL COleClientItem::Reload() { // first, close the object Close(); // release any pointers we have to the object RELEASE(m_lpObject); RELEASE(m_lpViewObject); // then reload the object with OleLoad and finish creation process BOOL bResult = FinishCreate(::OleLoad(m_lpStorage, IID_IUnknown, NULL, (LPLP)&m_lpObject)); ASSERT_VALID(this); return bResult; } BOOL COleClientItem::ActivateAs(LPCTSTR lpszUserType, REFCLSID clsidOld, REFCLSID clsidNew) { ASSERT_VALID(this); ASSERT(lpszUserType == NULL || AfxIsValidString(lpszUserType)); ASSERT(m_lpObject != NULL); // enable activate as m_scLast = _AfxOleDoTreatAsClass(lpszUserType, clsidOld, clsidNew); if (FAILED(m_scLast)) return FALSE; // reload all items in this doucment COleDocument* pDoc = GetDocument(); ASSERT_VALID(pDoc); POSITION pos = pDoc->GetStartPosition(); COleClientItem* pItem; while ((pItem = pDoc->GetNextClientItem(pos)) != NULL) { // reload it, so activate as works as appropriate pItem->Reload(); } ASSERT_VALID(this); return TRUE; } ///////////////////////////////////////////////////////////////////////////// // COleClientItem printing support BOOL COleClientItem::SetPrintDevice(const DVTARGETDEVICE* ptd) { ASSERT(ptd == NULL || AfxIsValidAddress(ptd, sizeof(DVTARGETDEVICE), FALSE)); // get printer device information from cache LPOLECACHE lpOleCache; DVTARGETDEVICE* ptdCur = NULL; DWORD dwConnection; if (!GetPrintDeviceInfo(&lpOleCache, &ptdCur, &dwConnection)) { lpOleCache = QUERYINTERFACE(m_lpObject, IOleCache); if (lpOleCache == NULL) return FALSE; // no print device info available } ASSERT(lpOleCache != NULL); // both may have no target device (considered equal) if (ptd == NULL && ptdCur == NULL) { lpOleCache->Release(); CoTaskMemFree(ptdCur); return TRUE; } if (ptd != NULL && ptdCur != NULL) { // should be non-NULL and valid addresses ASSERT(AfxIsValidAddress(ptd, (size_t)ptd->tdSize)); ASSERT(AfxIsValidAddress(ptdCur, (size_t)ptdCur->tdSize)); // see if they compare equal if (ptdCur->tdSize == ptd->tdSize && memcmp(ptdCur, ptd, (size_t)ptd->tdSize) == 0) { lpOleCache->Release(); CoTaskMemFree(ptdCur); return TRUE; } } // calling this with NULL will just remove the prevous printer cache if (ptd != NULL) { // new cache is for CF_METAFILEPICT, DVASPECT_CONTENT FORMATETC formatEtc; formatEtc.cfFormat = CF_METAFILEPICT; formatEtc.ptd = (DVTARGETDEVICE*)ptd; formatEtc.dwAspect = DVASPECT_CONTENT; formatEtc.lindex = -1; formatEtc.tymed = TYMED_MFPICT; // attempt to cache new format DWORD dwNewConnection; if (lpOleCache->Cache(&formatEtc, ADVFCACHE_ONSAVE, &dwNewConnection) != S_OK) { lpOleCache->Release(); CoTaskMemFree(ptdCur); return FALSE; } } // new format is cached successfully, uncache old format if (ptdCur != NULL) { lpOleCache->Uncache(dwConnection); CoTaskMemFree(ptdCur); } // cleanup & return lpOleCache->Release(); return TRUE; } BOOL COleClientItem::SetPrintDevice(const PRINTDLG* ppd) { ASSERT(ppd == NULL || AfxIsValidAddress(ppd, sizeof(*ppd), FALSE)); DVTARGETDEVICE* ptd = NULL; if (ppd != NULL) ptd = _AfxOleCreateTargetDevice((PRINTDLG*)ppd); BOOL bResult = SetPrintDevice(ptd); CoTaskMemFree(ptd); return bResult; } ///////////////////////////////////////////////////////////////////////////// // other advanced COleClientItem support void COleClientItem::GetUserType( USERCLASSTYPE nUserClassType, CString& rString) { ASSERT_VALID(this); ASSERT(m_lpObject != NULL); LPOLESTR lpszUserType; CheckGeneral(m_lpObject->GetUserType(nUserClassType, &lpszUserType)); ASSERT(lpszUserType != NULL); ASSERT(AfxIsValidString(lpszUserType)); rString = lpszUserType; CoTaskMemFree(lpszUserType); } void COleClientItem::Run() { ASSERT_VALID(this); ASSERT(m_lpObject != NULL); // is object already in running state? if (::OleIsRunning(m_lpObject)) return; // run the object -- throw exception on errors SCODE sc = ::OleRun(m_lpObject); CheckGeneral(sc); // should be running now ASSERT(::OleIsRunning(m_lpObject)); } ///////////////////////////////////////////////////////////////////////////// // Linked COleClientItem operations BOOL COleClientItem::UpdateLink() { ASSERT_VALID(this); ASSERT(m_lpObject != NULL); m_scLast = S_OK; if (!IsLinkUpToDate()) { m_scLast = m_lpObject->Update(); ASSERT_VALID(m_pDocument); m_pDocument->SetModifiedFlag(); } return m_scLast == S_OK; } BOOL COleClientItem::FreezeLink() { ASSERT_VALID(this); ASSERT(m_lpObject != NULL); ASSERT(m_pDocument != NULL); ASSERT(GetType() == OT_LINK); // first save & close the item Close(); // get IDataObject interface LPDATAOBJECT lpDataObject = QUERYINTERFACE(m_lpObject, IDataObject); ASSERT(lpDataObject != NULL); COleDataObject dataObject; dataObject.Attach(lpDataObject, TRUE); // save important state of original item LPOLEOBJECT lpObject = m_lpObject; LPSTORAGE lpStorage = m_lpStorage; LPLOCKBYTES lpLockBytes = m_lpLockBytes; LPVIEWOBJECT2 lpViewObject = m_lpViewObject; DWORD dwConnection = m_dwConnection; DWORD dwItemNumber = m_dwItemNumber; m_lpObject = NULL; m_lpStorage = NULL; m_lpLockBytes = NULL; m_lpViewObject = NULL; m_dwConnection = 0; // attempt to create new object from data if (!CreateStaticFromData(&dataObject)) { m_lpObject = lpObject; m_lpStorage = lpStorage; m_lpLockBytes = lpLockBytes; m_lpViewObject = lpViewObject; m_dwConnection = dwConnection; return FALSE; } #ifdef _DEBUG UpdateItemType(); ASSERT(GetType() == OT_STATIC); #endif // save new state of that item LPOLEOBJECT lpNewObject = m_lpObject; LPSTORAGE lpNewStorage = m_lpStorage; LPLOCKBYTES lpNewLockBytes = m_lpLockBytes; LPVIEWOBJECT2 lpNewViewObject = m_lpViewObject; DWORD dwNewConnection = m_dwConnection; DWORD dwNewItemNumber = m_dwItemNumber; // shut down old item m_lpObject = lpObject; m_lpStorage = lpStorage; m_lpLockBytes = lpLockBytes; m_lpViewObject = lpViewObject; m_dwConnection = dwConnection; m_dwItemNumber = dwItemNumber; #ifdef _DEBUG UpdateItemType(); ASSERT(GetType() == OT_LINK); #endif Delete(FALSE); // revokes item & removes storage // switch to new item m_lpObject = lpNewObject; m_lpStorage = lpNewStorage; m_lpLockBytes = lpNewLockBytes; m_lpViewObject = lpNewViewObject; m_dwConnection = dwNewConnection; m_dwItemNumber = dwNewItemNumber; UpdateItemType(); ASSERT(GetType() == OT_STATIC); // send an on changed with same state to invalidate the item OnChange(OLE_CHANGED_STATE, (DWORD)GetItemState()); ASSERT_VALID(m_pDocument); m_pDocument->SetModifiedFlag(); return TRUE; } ///////////////////////////////////////////////////////////////////////////// // Special link attributes OLEUPDATE COleClientItem::GetLinkUpdateOptions() { ASSERT_VALID(this); ASSERT(m_lpObject != NULL); LPOLELINK lpOleLink = QUERYINTERFACE(m_lpObject, IOleLink); ASSERT(lpOleLink != NULL); // perhaps not a link? DWORD dwUpdateOpt; SCODE sc = lpOleLink->GetUpdateOptions(&dwUpdateOpt); lpOleLink->Release(); CheckGeneral(sc); // may throw an exception return (OLEUPDATE)dwUpdateOpt; } void COleClientItem::SetLinkUpdateOptions(OLEUPDATE dwUpdateOpt) { ASSERT_VALID(this); ASSERT(m_lpObject != NULL); LPOLELINK lpOleLink = QUERYINTERFACE(m_lpObject, IOleLink); ASSERT(lpOleLink != NULL); // perhaps not a link? SCODE sc = lpOleLink->SetUpdateOptions(dwUpdateOpt); lpOleLink->Release(); CheckGeneral(sc); } /////////////////////////////////////////////////////////////////////////////