mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2133 lines
54 KiB
2133 lines
54 KiB
// This is a part of the Microsoft Foundation Classes C++ library.
|
|
// Copyright (C) 1992-1995 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 - Container view of IOleObject and related interfaces
|
|
|
|
COleClientItem::COleClientItem(COleDocument* pContainerDoc)
|
|
{
|
|
if (pContainerDoc != NULL)
|
|
ASSERT_VALID(pContainerDoc);
|
|
|
|
// initialize OLE client side view of IOleObject
|
|
m_lpObject = NULL;
|
|
m_lpViewObject = NULL;
|
|
m_dwConnection = 0;
|
|
m_lpStorage = NULL;
|
|
m_lpLockBytes = NULL;
|
|
m_scLast = S_OK;
|
|
m_pView = NULL;
|
|
m_pInPlaceFrame = NULL;
|
|
m_pInPlaceDoc = NULL;
|
|
m_nItemState = emptyState; // initially empty until OleLoad, OleCreate
|
|
m_bMoniker = FALSE;
|
|
m_nDrawAspect = DVASPECT_CONTENT; // default draw aspect
|
|
m_dwItemNumber = 0;
|
|
m_bLinkUnavail = FALSE; // set to TRUE on failed DoVerb, or in links dialog
|
|
m_nItemType = OT_UNKNOWN; // type unknown so far
|
|
m_hWndServer = NULL;
|
|
m_bClosing = FALSE; // COleClientItem::Close in process
|
|
m_bLocked = FALSE; // need CoLockObjectExternal(..., FALSE, ...)
|
|
|
|
// initialize compound file support
|
|
m_lpNewStorage = NULL;
|
|
m_bNeedCommit = FALSE;
|
|
|
|
if (pContainerDoc != NULL)
|
|
pContainerDoc->AddItem(this);
|
|
|
|
ASSERT(m_pDocument == pContainerDoc);
|
|
ASSERT_VALID(this);
|
|
|
|
AfxOleLockApp();
|
|
}
|
|
|
|
COleClientItem::~COleClientItem()
|
|
{
|
|
ASSERT_VALID(this);
|
|
|
|
// release any references we may have to other objects
|
|
Release();
|
|
|
|
// only remove it from the associated document if it hasn't been detached
|
|
// from the document already!
|
|
if (m_pDocument != NULL)
|
|
m_pDocument->RemoveItem(this);
|
|
|
|
// make sure all outside connections are disconnected
|
|
ExternalDisconnect();
|
|
AfxOleUnlockApp();
|
|
}
|
|
|
|
void COleClientItem::Delete(BOOL bAutoDelete)
|
|
{
|
|
USES_CONVERSION;
|
|
ASSERT_VALID(this);
|
|
|
|
Release(); // first close it
|
|
|
|
COleDocument* pDoc = GetDocument();
|
|
if (pDoc != NULL && pDoc->m_bCompoundFile)
|
|
{
|
|
// cleanup docfile storage first
|
|
COleDocument* pDoc = GetDocument();
|
|
ASSERT_VALID(pDoc);
|
|
|
|
if (pDoc->m_lpRootStg != NULL)
|
|
{
|
|
// get item name
|
|
TCHAR szItemName[OLE_MAXITEMNAME];
|
|
GetItemName(szItemName);
|
|
|
|
// attempt to remove it from the storage, ignore errors
|
|
pDoc->m_lpRootStg->DestroyElement(T2COLE(szItemName));
|
|
}
|
|
}
|
|
|
|
if (bAutoDelete)
|
|
{
|
|
// remove item from document
|
|
if (pDoc != NULL)
|
|
pDoc->RemoveItem(this);
|
|
|
|
InternalRelease(); // remove the item from memory
|
|
}
|
|
}
|
|
|
|
void COleClientItem::Release(OLECLOSE dwCloseOption)
|
|
{
|
|
ASSERT_VALID(this);
|
|
|
|
m_scLast = S_OK;
|
|
|
|
// cleanup view advise
|
|
if (m_lpViewObject != NULL)
|
|
{
|
|
DWORD dwAspect;
|
|
VERIFY(m_lpViewObject->GetAdvise(&dwAspect, NULL, NULL) == S_OK);
|
|
VERIFY(m_lpViewObject->SetAdvise(dwAspect, 0, NULL) == S_OK);
|
|
RELEASE(m_lpViewObject);
|
|
}
|
|
|
|
// cleanup the OLE object itself
|
|
if (m_lpObject != NULL)
|
|
{
|
|
// cleanup object advise
|
|
if (m_dwConnection != 0)
|
|
{
|
|
VERIFY(m_lpObject->Unadvise(m_dwConnection) == S_OK);
|
|
m_dwConnection = 0;
|
|
}
|
|
|
|
// close object and save (except now when called from destructor)
|
|
// (NOTE: errors are _not_ reported as an exception)
|
|
m_scLast = m_lpObject->Close(dwCloseOption);
|
|
RELEASE(m_lpObject);
|
|
}
|
|
|
|
// cleanup storage related data
|
|
RELEASE(m_lpStorage);
|
|
RELEASE(m_lpLockBytes);
|
|
|
|
// cleanup in-place editing data
|
|
if (m_pInPlaceFrame != NULL)
|
|
{
|
|
m_pInPlaceFrame->InternalRelease();
|
|
m_pInPlaceFrame = NULL;
|
|
if (m_pInPlaceDoc != NULL)
|
|
{
|
|
m_pInPlaceDoc->InternalRelease();
|
|
m_pInPlaceDoc = NULL;
|
|
}
|
|
}
|
|
ASSERT(m_pInPlaceFrame == NULL);
|
|
ASSERT(m_pInPlaceDoc == NULL);
|
|
}
|
|
|
|
void COleClientItem::Close(OLECLOSE dwCloseOption)
|
|
{
|
|
ASSERT_VALID(this);
|
|
ASSERT(m_lpObject != NULL);
|
|
|
|
// gaurd against re-entry
|
|
if (m_bClosing)
|
|
return;
|
|
|
|
m_bClosing = TRUE;
|
|
|
|
// attempt to close the object
|
|
#ifdef _MAC
|
|
// Some Mac servers (Excel) fail to allow the IP deactivation task switch
|
|
// to finish before issuing an RPC call, which gets Mac LRPC out of sync.
|
|
// Effectively suspend this thread until the RPC channel gets back in sync
|
|
int nTries = 0;
|
|
do
|
|
{
|
|
m_scLast = m_lpObject->Close(dwCloseOption);
|
|
nTries++;
|
|
} while (FAILED(m_scLast) && nTries < 10);
|
|
#else
|
|
m_scLast = m_lpObject->Close(dwCloseOption);
|
|
#endif
|
|
|
|
// remove external lock placed on item during in-place activation
|
|
if (m_bLocked)
|
|
{
|
|
OleLockRunning(m_lpObject, FALSE, TRUE);
|
|
m_bLocked = FALSE;
|
|
}
|
|
|
|
// handle failure cases -- COleClientItem::Close can be used to
|
|
// robustly handle a server crashing (ie. something unexpected happens,
|
|
// we'll call COleClientItem::Close to attempt safe shutdown)
|
|
if (GetItemState() != loadedState)
|
|
{
|
|
// We'll call COleClientItem::Close anywhere a catastrophe
|
|
// happens inside of other portions of COleClientItem. We must
|
|
// completely exit from any in-place/open state.
|
|
|
|
// force transition from activeUIState to activeState
|
|
if (GetItemState() == activeUIState)
|
|
OnDeactivateUI(FALSE);
|
|
|
|
// force transition from activeState to loadedState
|
|
if (GetItemState() == activeState)
|
|
OnDeactivate();
|
|
|
|
if (m_nItemState != loadedState)
|
|
{
|
|
// in case of extreme failure, force loadedState
|
|
OnChange(OLE_CHANGED_STATE, (DWORD)loadedState);
|
|
m_nItemState = loadedState; // force it to loaded state
|
|
}
|
|
}
|
|
|
|
m_bClosing = FALSE; // now safe for further close calls
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// COleClientItem name management
|
|
|
|
DWORD COleClientItem::GetNewItemNumber()
|
|
{
|
|
ASSERT_VALID(this);
|
|
|
|
COleDocument* pDoc = GetDocument();
|
|
ASSERT_VALID(pDoc);
|
|
DWORD dwNextItemNumber = pDoc->m_dwNextItemNumber;
|
|
|
|
for (;;)
|
|
{
|
|
// make sure that m_dwNextItemNumber is not used in another item first
|
|
POSITION pos = pDoc->GetStartPosition();
|
|
COleClientItem* pItem;
|
|
while ((pItem = pDoc->GetNextClientItem(pos)) != NULL)
|
|
{
|
|
if (pItem->m_dwItemNumber == dwNextItemNumber)
|
|
break;
|
|
}
|
|
if (pItem == NULL)
|
|
break; // no item using m_dwNextItemNumber
|
|
|
|
// m_dwNextItemNumber is in use, bump to next one!
|
|
++dwNextItemNumber;
|
|
}
|
|
|
|
pDoc->m_dwNextItemNumber = dwNextItemNumber + 1;
|
|
return dwNextItemNumber;
|
|
}
|
|
|
|
void COleClientItem::GetItemName(LPTSTR lpszItemName) const
|
|
{
|
|
ASSERT_VALID(this);
|
|
ASSERT(lpszItemName != NULL);
|
|
|
|
wsprintf(lpszItemName, _T("Embedding %lu"), m_dwItemNumber);
|
|
ASSERT(lstrlen(lpszItemName) < OLE_MAXITEMNAME);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// COleClientItem creation helpers
|
|
|
|
void COleClientItem::UpdateItemType()
|
|
{
|
|
ASSERT_VALID(this);
|
|
ASSERT(m_lpObject != NULL);
|
|
|
|
// check for linked object
|
|
LPOLELINK lpOleLink = QUERYINTERFACE(m_lpObject, IOleLink);
|
|
if (lpOleLink != NULL)
|
|
{
|
|
lpOleLink->Release();
|
|
m_nItemType = OT_LINK;
|
|
return;
|
|
}
|
|
|
|
// check for static object
|
|
DWORD dwStatus;
|
|
if (m_lpObject->GetMiscStatus(DVASPECT_CONTENT, &dwStatus) == S_OK
|
|
&& (dwStatus & OLEMISC_STATIC) == 0)
|
|
{
|
|
m_nItemType = OT_EMBEDDED;
|
|
return;
|
|
}
|
|
|
|
// not not link, not embedding -- must be static
|
|
m_nItemType = OT_STATIC;
|
|
}
|
|
|
|
BOOL COleClientItem::FinishCreate(SCODE sc)
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
ASSERT_VALID(this);
|
|
ASSERT(m_pView == NULL);
|
|
|
|
TRY
|
|
{
|
|
// m_lpObject is currently an IUnknown, convert to IOleObject
|
|
if (m_lpObject != NULL)
|
|
{
|
|
LPUNKNOWN lpUnk = m_lpObject;
|
|
m_lpObject = QUERYINTERFACE(lpUnk, IOleObject);
|
|
lpUnk->Release();
|
|
if (m_lpObject == NULL)
|
|
AfxThrowOleException(E_OUTOFMEMORY);
|
|
}
|
|
|
|
// check return code from create function
|
|
CheckGeneral(sc);
|
|
|
|
UpdateItemType();
|
|
|
|
// cache the IViewObject interface
|
|
m_lpViewObject = QUERYINTERFACE(m_lpObject, IViewObject2);
|
|
if (m_lpViewObject == NULL)
|
|
CheckGeneral(E_NOINTERFACE);
|
|
ASSERT(m_lpViewObject != NULL);
|
|
|
|
if (GetType() != OT_STATIC)
|
|
{
|
|
// setup for advises; we assume that OLE cleans them up properly
|
|
LPADVISESINK lpAdviseSink =
|
|
(LPADVISESINK)GetInterface(&IID_IAdviseSink);
|
|
ASSERT(lpAdviseSink != NULL);
|
|
CheckGeneral(m_lpObject->Advise(lpAdviseSink, &m_dwConnection));
|
|
ASSERT(m_dwConnection != 0);
|
|
|
|
// set up view advise
|
|
VERIFY(m_lpViewObject->SetAdvise(DVASPECT_CONTENT, 0, lpAdviseSink)
|
|
== S_OK);
|
|
|
|
// the server shows these in its user-interface
|
|
// (as document title and in File Exit menu)
|
|
m_lpObject->SetHostNames(T2COLE(AfxGetAppName()),
|
|
T2COLE(m_pDocument->GetTitle()));
|
|
}
|
|
|
|
// all items are "contained" -- this makes our reference to this object
|
|
// weak -- which is needed for links to embedding silent update.
|
|
OleSetContainedObject(m_lpObject, TRUE);
|
|
|
|
// considered loaded at this point
|
|
m_nItemState = loadedState;
|
|
}
|
|
CATCH_ALL(e)
|
|
{
|
|
Release(); // release the object just in case
|
|
ASSERT_VALID(this);
|
|
DELETE_EXCEPTION(e);
|
|
return FALSE;
|
|
}
|
|
END_CATCH_ALL
|
|
|
|
// set state to loaded
|
|
ASSERT(m_nItemState != emptyState);
|
|
|
|
// otherwise no errors, return success!
|
|
ASSERT_VALID(this);
|
|
return TRUE;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// COleClientItem create API variants
|
|
|
|
BOOL COleClientItem::CreateFromClipboard(
|
|
OLERENDER render, CLIPFORMAT cfFormat, LPFORMATETC lpFormatEtc)
|
|
{
|
|
ASSERT_VALID(this);
|
|
ASSERT(m_lpObject == NULL); // one time only
|
|
ASSERT(m_pDocument != NULL);
|
|
ASSERT(lpFormatEtc == NULL ||
|
|
AfxIsValidAddress(lpFormatEtc, sizeof(FORMATETC), FALSE));
|
|
|
|
// get clipboard contents
|
|
COleDataObject dataObject;
|
|
if (!dataObject.AttachClipboard())
|
|
return FALSE;
|
|
|
|
// create from IDataObject
|
|
BOOL bResult = CreateFromData(&dataObject, render, cfFormat, lpFormatEtc);
|
|
|
|
ASSERT_VALID(this);
|
|
return bResult;
|
|
}
|
|
|
|
BOOL COleClientItem::CreateLinkFromClipboard(
|
|
OLERENDER render, CLIPFORMAT cfFormat, LPFORMATETC lpFormatEtc)
|
|
{
|
|
ASSERT_VALID(this);
|
|
ASSERT(m_lpObject == NULL); // one time only
|
|
ASSERT(m_pDocument != NULL);
|
|
ASSERT(lpFormatEtc == NULL ||
|
|
AfxIsValidAddress(lpFormatEtc, sizeof(FORMATETC), FALSE));
|
|
|
|
// get clipboard contents
|
|
COleDataObject dataObject;
|
|
if (!dataObject.AttachClipboard())
|
|
return FALSE;
|
|
|
|
// create from IDataObject
|
|
BOOL bResult = CreateLinkFromData(&dataObject, render, cfFormat, lpFormatEtc);
|
|
ASSERT_VALID(this);
|
|
|
|
return bResult;
|
|
}
|
|
|
|
BOOL COleClientItem::CreateStaticFromClipboard(
|
|
OLERENDER render, CLIPFORMAT cfFormat, LPFORMATETC lpFormatEtc)
|
|
{
|
|
ASSERT_VALID(this);
|
|
ASSERT(m_lpObject == NULL); // one time only
|
|
ASSERT(m_pDocument != NULL);
|
|
ASSERT(lpFormatEtc == NULL ||
|
|
AfxIsValidAddress(lpFormatEtc, sizeof(FORMATETC), FALSE));
|
|
|
|
// get clipboard contents
|
|
COleDataObject dataObject;
|
|
if (!dataObject.AttachClipboard())
|
|
return FALSE;
|
|
|
|
// create from IDataObject
|
|
BOOL bResult = CreateStaticFromData(&dataObject, render, cfFormat,
|
|
lpFormatEtc);
|
|
|
|
ASSERT_VALID(this);
|
|
return bResult;
|
|
}
|
|
|
|
// Creation from IDataObject (used for drag-drop)
|
|
BOOL COleClientItem::CreateFromData(COleDataObject* pDataObject,
|
|
OLERENDER render, CLIPFORMAT cfFormat, LPFORMATETC lpFormatEtc)
|
|
{
|
|
ASSERT_VALID(this);
|
|
ASSERT(m_lpObject == NULL); // one time only
|
|
ASSERT(m_pDocument != NULL);
|
|
ASSERT(lpFormatEtc == NULL ||
|
|
AfxIsValidAddress(lpFormatEtc, sizeof(FORMATETC), FALSE));
|
|
|
|
// get storage for the object via virtual function call
|
|
m_dwItemNumber = GetNewItemNumber();
|
|
GetItemStorage();
|
|
ASSERT(m_lpStorage != NULL);
|
|
|
|
// fill in FORMATETC struct
|
|
FORMATETC formatEtc;
|
|
lpFormatEtc = _AfxFillFormatEtc(lpFormatEtc, cfFormat, &formatEtc);
|
|
|
|
// attempt to create the object
|
|
LPOLECLIENTSITE lpClientSite = GetClientSite();
|
|
LPDATAOBJECT lpDataObject = pDataObject->GetIDataObject(FALSE);
|
|
SCODE sc = ::OleCreateFromData(lpDataObject, IID_IUnknown, render,
|
|
lpFormatEtc, lpClientSite, m_lpStorage, (LPLP)&m_lpObject);
|
|
BOOL bResult = FinishCreate(sc);
|
|
|
|
ASSERT_VALID(this);
|
|
return bResult;
|
|
}
|
|
|
|
BOOL COleClientItem::CreateLinkFromData(COleDataObject* pDataObject,
|
|
OLERENDER render, CLIPFORMAT cfFormat, LPFORMATETC lpFormatEtc)
|
|
{
|
|
ASSERT_VALID(this);
|
|
ASSERT(m_lpObject == NULL); // one time only
|
|
ASSERT(m_pDocument != NULL);
|
|
ASSERT(lpFormatEtc == NULL ||
|
|
AfxIsValidAddress(lpFormatEtc, sizeof(FORMATETC), FALSE));
|
|
|
|
// get storage for the object via virtual function call
|
|
m_dwItemNumber = GetNewItemNumber();
|
|
GetItemStorage();
|
|
ASSERT(m_lpStorage != NULL);
|
|
|
|
// fill in FORMATETC struct
|
|
FORMATETC formatEtc;
|
|
lpFormatEtc = _AfxFillFormatEtc(lpFormatEtc, cfFormat, &formatEtc);
|
|
|
|
// attempt to create the link
|
|
LPOLECLIENTSITE lpClientSite = GetClientSite();
|
|
LPDATAOBJECT lpDataObject = pDataObject->GetIDataObject(FALSE);
|
|
SCODE sc = ::OleCreateLinkFromData(lpDataObject, IID_IUnknown,
|
|
render, lpFormatEtc, lpClientSite, m_lpStorage, (LPLP)&m_lpObject);
|
|
BOOL bResult = FinishCreate(sc);
|
|
|
|
ASSERT_VALID(this);
|
|
return bResult;
|
|
}
|
|
|
|
BOOL COleClientItem::CreateStaticFromData(COleDataObject* pDataObject,
|
|
OLERENDER render, CLIPFORMAT cfFormat, LPFORMATETC lpFormatEtc)
|
|
{
|
|
ASSERT_VALID(this);
|
|
ASSERT(m_lpObject == NULL); // one time only
|
|
ASSERT(m_pDocument != NULL);
|
|
ASSERT(lpFormatEtc == NULL ||
|
|
AfxIsValidAddress(lpFormatEtc, sizeof(FORMATETC), FALSE));
|
|
|
|
// get storage for the object via virtual function call
|
|
m_dwItemNumber = GetNewItemNumber();
|
|
GetItemStorage();
|
|
ASSERT(m_lpStorage != NULL);
|
|
|
|
// fill in FORMATETC struct
|
|
FORMATETC formatEtc;
|
|
lpFormatEtc = _AfxFillFormatEtc(lpFormatEtc, cfFormat, &formatEtc);
|
|
|
|
// attempt to create the link
|
|
LPOLECLIENTSITE lpClientSite = GetClientSite();
|
|
LPDATAOBJECT lpDataObject = pDataObject->GetIDataObject(FALSE);
|
|
SCODE sc = ::OleCreateStaticFromData(lpDataObject, IID_IUnknown,
|
|
render, lpFormatEtc, lpClientSite, m_lpStorage, (LPLP)&m_lpObject);
|
|
BOOL bResult = FinishCreate(sc);
|
|
|
|
ASSERT_VALID(this);
|
|
return bResult;
|
|
}
|
|
|
|
// Creation from files (in OLE 1.0, the user did this through packager)
|
|
BOOL COleClientItem::CreateFromFile(LPCTSTR lpszFileName, REFCLSID clsid,
|
|
OLERENDER render, CLIPFORMAT cfFormat, LPFORMATETC lpFormatEtc)
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
ASSERT_VALID(this);
|
|
ASSERT(m_lpObject == NULL); // one time only
|
|
ASSERT(m_pDocument != NULL);
|
|
ASSERT(lpFormatEtc == NULL ||
|
|
AfxIsValidAddress(lpFormatEtc, sizeof(FORMATETC), FALSE));
|
|
|
|
// get storage for the object via virtual function call
|
|
m_dwItemNumber = GetNewItemNumber();
|
|
GetItemStorage();
|
|
ASSERT(m_lpStorage != NULL);
|
|
|
|
// fill in FORMATETC struct
|
|
FORMATETC formatEtc;
|
|
lpFormatEtc = _AfxFillFormatEtc(lpFormatEtc, cfFormat, &formatEtc);
|
|
|
|
// attempt to create the object
|
|
LPOLECLIENTSITE lpClientSite = GetClientSite();
|
|
SCODE sc = ::OleCreateFromFile(clsid, T2COLE(lpszFileName),
|
|
IID_IUnknown, render, lpFormatEtc, lpClientSite, m_lpStorage,
|
|
(LPLP)&m_lpObject);
|
|
BOOL bResult = FinishCreate(sc);
|
|
|
|
ASSERT_VALID(this);
|
|
return bResult;
|
|
}
|
|
|
|
BOOL COleClientItem::CreateLinkFromFile(LPCTSTR lpszFileName,
|
|
OLERENDER render, CLIPFORMAT cfFormat, LPFORMATETC lpFormatEtc)
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
ASSERT_VALID(this);
|
|
ASSERT(m_lpObject == NULL); // one time only
|
|
ASSERT(m_pDocument != NULL);
|
|
ASSERT(lpFormatEtc == NULL ||
|
|
AfxIsValidAddress(lpFormatEtc, sizeof(FORMATETC), FALSE));
|
|
|
|
// get storage for the object via virtual function call
|
|
m_dwItemNumber = GetNewItemNumber();
|
|
GetItemStorage();
|
|
ASSERT(m_lpStorage != NULL);
|
|
|
|
// fill in FORMATETC struct
|
|
FORMATETC formatEtc;
|
|
lpFormatEtc = _AfxFillFormatEtc(lpFormatEtc, cfFormat, &formatEtc);
|
|
|
|
// attempt to create the link
|
|
LPOLECLIENTSITE lpClientSite = GetClientSite();
|
|
SCODE sc = ::OleCreateLinkToFile(T2COLE(lpszFileName),
|
|
IID_IUnknown, render, lpFormatEtc, lpClientSite, m_lpStorage,
|
|
(LPLP)&m_lpObject);
|
|
BOOL bResult = FinishCreate(sc);
|
|
|
|
ASSERT_VALID(this);
|
|
return bResult;
|
|
}
|
|
|
|
// create from class name (for insert item dialog)
|
|
BOOL COleClientItem::CreateNewItem(REFCLSID clsid,
|
|
OLERENDER render, CLIPFORMAT cfFormat, LPFORMATETC lpFormatEtc)
|
|
{
|
|
ASSERT_VALID(this);
|
|
ASSERT(m_lpObject == NULL); // one time only
|
|
ASSERT(m_pDocument != NULL);
|
|
ASSERT(lpFormatEtc == NULL ||
|
|
AfxIsValidAddress(lpFormatEtc, sizeof(FORMATETC), FALSE));
|
|
|
|
// get storage for the object via virtual function call
|
|
m_dwItemNumber = GetNewItemNumber();
|
|
GetItemStorage();
|
|
ASSERT(m_lpStorage != NULL);
|
|
|
|
// fill in FORMATETC struct
|
|
FORMATETC formatEtc;
|
|
lpFormatEtc = _AfxFillFormatEtc(lpFormatEtc, cfFormat, &formatEtc);
|
|
|
|
// attempt to create the object
|
|
LPOLECLIENTSITE lpClientSite = GetClientSite();
|
|
SCODE sc = ::OleCreate(clsid, IID_IUnknown,
|
|
render, lpFormatEtc, lpClientSite, m_lpStorage, (LPLP)&m_lpObject);
|
|
BOOL bResult = FinishCreate(sc);
|
|
|
|
ASSERT_VALID(this);
|
|
return bResult;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// More advanced creation
|
|
|
|
BOOL COleClientItem::CreateCloneFrom(const COleClientItem* pSrcItem)
|
|
{
|
|
ASSERT_VALID(this);
|
|
ASSERT(m_lpObject == NULL); // one time only
|
|
ASSERT_VALID(pSrcItem);
|
|
ASSERT(m_pDocument != NULL);
|
|
|
|
// create storage for the item
|
|
m_dwItemNumber = GetNewItemNumber();
|
|
GetItemStorage();
|
|
ASSERT(m_lpStorage != NULL);
|
|
|
|
// save the object first
|
|
LPPERSISTSTORAGE lpPersistStorage =
|
|
QUERYINTERFACE(pSrcItem->m_lpObject, IPersistStorage);
|
|
ASSERT(lpPersistStorage != NULL);
|
|
SCODE sc = ::OleSave(lpPersistStorage, m_lpStorage, FALSE);
|
|
lpPersistStorage->SaveCompleted(NULL);
|
|
lpPersistStorage->Release();
|
|
if (sc != S_OK)
|
|
{
|
|
// failed the save, do not attempt to create clone
|
|
m_scLast = sc;
|
|
return FALSE;
|
|
}
|
|
|
|
// get information on the view advise type
|
|
ASSERT(pSrcItem->m_lpViewObject != NULL);
|
|
DWORD dwAspect;
|
|
VERIFY(pSrcItem->m_lpViewObject->GetAdvise(&dwAspect, NULL, NULL) == S_OK);
|
|
|
|
// then load the new object from the new storage
|
|
LPOLECLIENTSITE lpClientSite = GetClientSite();
|
|
sc = ::OleLoad(m_lpStorage, IID_IUnknown,
|
|
lpClientSite, (LPLP)&m_lpObject);
|
|
BOOL bResult = FinishCreate(sc);
|
|
|
|
ASSERT_VALID(this);
|
|
return bResult;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Storage for COleClientItem objects (memory and compound files)
|
|
|
|
BOOL COleClientItem::IsModified() const
|
|
{
|
|
ASSERT_VALID(this);
|
|
ASSERT(m_lpObject != NULL);
|
|
|
|
// get IPersistStorage interface, and call IsDirty
|
|
LPPERSISTSTORAGE lpPersistStorage =
|
|
QUERYINTERFACE(m_lpObject, IPersistStorage);
|
|
ASSERT(lpPersistStorage != NULL);
|
|
SCODE sc = lpPersistStorage->IsDirty();
|
|
lpPersistStorage->Release();
|
|
|
|
// S_OK == S_TRUE, therefore object dirty!
|
|
return sc == S_OK || FAILED(sc);
|
|
}
|
|
|
|
void COleClientItem::GetItemStorageFlat()
|
|
{
|
|
ASSERT_VALID(this);
|
|
ASSERT(m_lpStorage == NULL);
|
|
ASSERT(m_lpLockBytes == NULL);
|
|
|
|
SCODE sc = ::CreateILockBytesOnHGlobal(NULL, TRUE, &m_lpLockBytes);
|
|
if (sc != S_OK)
|
|
AfxThrowOleException(sc);
|
|
ASSERT(m_lpLockBytes != NULL);
|
|
|
|
sc = ::StgCreateDocfileOnILockBytes(m_lpLockBytes,
|
|
STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_READWRITE, 0, &m_lpStorage);
|
|
if (sc != S_OK)
|
|
{
|
|
VERIFY(m_lpLockBytes->Release() == 0);
|
|
m_lpLockBytes = NULL;
|
|
AfxThrowOleException(sc);
|
|
}
|
|
ASSERT(m_lpStorage != NULL);
|
|
|
|
ASSERT_VALID(this);
|
|
}
|
|
|
|
void COleClientItem::ReadItemFlat(CArchive& ar)
|
|
{
|
|
ASSERT_VALID(this);
|
|
ASSERT(m_lpStorage == NULL);
|
|
ASSERT(m_lpLockBytes == NULL);
|
|
|
|
// read number of bytes in the ILockBytes
|
|
DWORD dwBytes;
|
|
ar >> dwBytes;
|
|
|
|
// allocate enough memory to read entire block
|
|
HGLOBAL hStorage = ::GlobalAlloc(GMEM_SHARE|GMEM_MOVEABLE, dwBytes);
|
|
if (hStorage == NULL)
|
|
AfxThrowMemoryException();
|
|
|
|
LPVOID lpBuf = ::GlobalLock(hStorage);
|
|
ASSERT(lpBuf != NULL);
|
|
DWORD dwBytesRead = ar.Read(lpBuf, dwBytes);
|
|
::GlobalUnlock(hStorage);
|
|
|
|
// throw exception in case of partial object
|
|
if (dwBytesRead != dwBytes)
|
|
{
|
|
::GlobalFree(hStorage);
|
|
AfxThrowArchiveException(CArchiveException::endOfFile);
|
|
}
|
|
|
|
SCODE sc = CreateILockBytesOnHGlobal(hStorage, TRUE, &m_lpLockBytes);
|
|
if (sc != S_OK)
|
|
{
|
|
::GlobalFree(hStorage);
|
|
AfxThrowOleException(sc);
|
|
}
|
|
ASSERT(m_lpLockBytes != NULL);
|
|
ASSERT(::StgIsStorageILockBytes(m_lpLockBytes) == S_OK);
|
|
|
|
sc = ::StgOpenStorageOnILockBytes(m_lpLockBytes, NULL,
|
|
STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &m_lpStorage);
|
|
if (sc != S_OK)
|
|
{
|
|
VERIFY(m_lpLockBytes->Release() == 0);
|
|
m_lpLockBytes = NULL;
|
|
// ILockBytes::Release will GlobalFree the hStorage
|
|
AfxThrowOleException(sc);
|
|
}
|
|
|
|
// attempt to load the object from the storage
|
|
LPUNKNOWN lpUnk = NULL;
|
|
sc = ::OleLoad(m_lpStorage, IID_IUnknown, GetClientSite(),
|
|
(LPLP)&lpUnk);
|
|
CheckGeneral(sc);
|
|
|
|
ASSERT(lpUnk != NULL);
|
|
m_lpObject = QUERYINTERFACE(lpUnk, IOleObject);
|
|
lpUnk->Release();
|
|
if (m_lpObject == NULL)
|
|
AfxThrowOleException(E_OUTOFMEMORY);
|
|
|
|
ASSERT_VALID(this);
|
|
}
|
|
|
|
void COleClientItem::WriteItemFlat(CArchive& ar)
|
|
{
|
|
ASSERT_VALID(this);
|
|
ASSERT(m_lpStorage != NULL);
|
|
ASSERT(m_lpLockBytes != NULL);
|
|
|
|
// save the OLE object to its storage first
|
|
LPPERSISTSTORAGE lpPersistStorage =
|
|
QUERYINTERFACE(m_lpObject, IPersistStorage);
|
|
ASSERT(lpPersistStorage != NULL);
|
|
SCODE sc;
|
|
if (GetDocument()->m_bCompoundFile || lpPersistStorage->IsDirty() == S_OK)
|
|
{
|
|
sc = ::OleSave(lpPersistStorage, m_lpStorage,
|
|
!GetDocument()->m_bCompoundFile);
|
|
lpPersistStorage->SaveCompleted(NULL);
|
|
}
|
|
lpPersistStorage->Release();
|
|
m_lpStorage->Commit(STGC_OVERWRITE);
|
|
ASSERT(::StgIsStorageILockBytes(m_lpLockBytes) == S_OK);
|
|
|
|
// attempt to get the handle to the global memory
|
|
HGLOBAL hStorage;
|
|
sc = ::GetHGlobalFromILockBytes(m_lpLockBytes, &hStorage);
|
|
if (sc != S_OK)
|
|
AfxThrowOleException(sc);
|
|
|
|
// first write a byte count
|
|
STATSTG statstg;
|
|
sc = m_lpLockBytes->Stat(&statstg, STATFLAG_NONAME);
|
|
if (sc != S_OK)
|
|
AfxThrowOleException(sc);
|
|
ASSERT(statstg.cbSize.HighPart == 0); // don't support >4GB objects
|
|
DWORD dwBytes = statstg.cbSize.LowPart;
|
|
ar << dwBytes;
|
|
|
|
// write the contents of the block
|
|
LPVOID lpBuf = GlobalLock(hStorage);
|
|
ASSERT(lpBuf != NULL);
|
|
ar.Write(lpBuf, (UINT)dwBytes);
|
|
::GlobalUnlock(hStorage);
|
|
#ifdef _MAC
|
|
char **ppch;
|
|
UnwrapHandle(hStorage, &ppch);
|
|
#endif
|
|
}
|
|
|
|
void COleClientItem::GetItemStorageCompound()
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
COleDocument* pDoc = GetDocument();
|
|
ASSERT_VALID(pDoc);
|
|
ASSERT(pDoc->m_bCompoundFile);
|
|
if (pDoc->m_lpRootStg == NULL)
|
|
{
|
|
ASSERT(pDoc->m_bEmbedded);
|
|
pDoc->m_bEmbedded = FALSE;
|
|
if (!pDoc->OnNewDocument())
|
|
{
|
|
TRACE0("Warning OnNewDocument failed during COleClientItem::CreateXXXX\n");
|
|
AfxThrowMemoryException();
|
|
}
|
|
}
|
|
ASSERT(pDoc->m_lpRootStg != NULL);
|
|
|
|
// get item name
|
|
TCHAR szItemName[OLE_MAXITEMNAME];
|
|
GetItemName(szItemName);
|
|
|
|
// create storage for this item
|
|
LPSTORAGE lpStorage;
|
|
SCODE sc = pDoc->m_lpRootStg->CreateStorage(T2COLE(szItemName),
|
|
STGM_CREATE|STGM_READWRITE|STGM_TRANSACTED|STGM_SHARE_EXCLUSIVE,
|
|
0, 0, &lpStorage);
|
|
if (sc != S_OK)
|
|
{
|
|
TRACE1("Warning: unable to create child storage %s.\n", szItemName);
|
|
// upon failure throw file exception (item will be cleaned up)
|
|
AfxThrowOleException(sc);
|
|
}
|
|
ASSERT(lpStorage != NULL);
|
|
|
|
// everything should have worked
|
|
m_lpStorage = lpStorage;
|
|
ASSERT(m_lpStorage != NULL);
|
|
}
|
|
|
|
void COleClientItem::ReadItemCompound(CArchive& ar)
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
COleDocument* pDoc = GetDocument();
|
|
ASSERT_VALID(pDoc);
|
|
ASSERT(pDoc->m_lpRootStg != NULL);
|
|
ASSERT(pDoc->m_bCompoundFile);
|
|
ASSERT(m_lpStorage == NULL);
|
|
ASSERT(m_lpLockBytes == NULL);
|
|
|
|
if (ar.m_bForceFlat)
|
|
{
|
|
ReadItemFlat(ar);
|
|
RELEASE(m_lpStorage);
|
|
RELEASE(m_lpLockBytes);
|
|
|
|
// change the number to something definitely unique
|
|
m_dwItemNumber = GetNewItemNumber();
|
|
|
|
// create new storage
|
|
GetItemStorageCompound();
|
|
LPPERSISTSTORAGE lpPersistStorage =
|
|
QUERYINTERFACE(m_lpObject, IPersistStorage);
|
|
ASSERT(lpPersistStorage != NULL);
|
|
SCODE sc = ::OleSave(lpPersistStorage, m_lpStorage, FALSE);
|
|
if (FAILED(sc))
|
|
{
|
|
lpPersistStorage->Release();
|
|
CheckGeneral(sc);
|
|
}
|
|
VERIFY(lpPersistStorage->SaveCompleted(m_lpStorage) == S_OK);
|
|
lpPersistStorage->Release();
|
|
}
|
|
else
|
|
{
|
|
// get item name
|
|
TCHAR szItemName[OLE_MAXITEMNAME];
|
|
GetItemName(szItemName);
|
|
|
|
// open storage for this item
|
|
LPSTORAGE lpStorage;
|
|
SCODE sc = pDoc->m_lpRootStg->OpenStorage(T2COLE(szItemName), NULL,
|
|
STGM_READWRITE|STGM_TRANSACTED|STGM_SHARE_EXCLUSIVE,
|
|
0, 0, &lpStorage);
|
|
if (sc != S_OK)
|
|
{
|
|
TRACE1("Warning: unable to open child storage %s.\n", szItemName);
|
|
// upon failure throw file exception (item will be cleaned up)
|
|
AfxThrowOleException(sc);
|
|
}
|
|
ASSERT(lpStorage != NULL);
|
|
|
|
// remember the storage
|
|
m_lpStorage = lpStorage;
|
|
ASSERT(m_lpStorage != NULL);
|
|
|
|
// attempt to load the object from the storage
|
|
LPUNKNOWN lpUnk = NULL;
|
|
sc = ::OleLoad(m_lpStorage, IID_IUnknown, GetClientSite(),
|
|
(LPLP)&lpUnk);
|
|
CheckGeneral(sc);
|
|
|
|
// get IOleObject interface for the newly loaded object
|
|
ASSERT(lpUnk != NULL);
|
|
m_lpObject = QUERYINTERFACE(lpUnk, IOleObject);
|
|
lpUnk->Release();
|
|
if (m_lpObject == NULL)
|
|
AfxThrowOleException(E_OUTOFMEMORY);
|
|
}
|
|
}
|
|
|
|
void COleClientItem::WriteItemCompound(CArchive& ar)
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
COleDocument* pDoc = GetDocument();
|
|
ASSERT_VALID(pDoc);
|
|
ASSERT(pDoc->m_lpRootStg != NULL);
|
|
ASSERT(pDoc->m_bCompoundFile);
|
|
ASSERT(m_lpNewStorage == NULL);
|
|
|
|
if (ar.m_bForceFlat)
|
|
{
|
|
LPSTORAGE pTempStorage = m_lpStorage;
|
|
LPLOCKBYTES pTempLockBytes = m_lpLockBytes;
|
|
m_lpStorage = NULL;
|
|
m_lpLockBytes = NULL;
|
|
GetItemStorageFlat();
|
|
WriteItemFlat(ar);
|
|
RELEASE(m_lpStorage);
|
|
RELEASE(m_lpLockBytes);
|
|
m_lpStorage = pTempStorage;
|
|
m_lpLockBytes = pTempLockBytes;
|
|
return;
|
|
}
|
|
|
|
// get item name
|
|
TCHAR szItemName[OLE_MAXITEMNAME];
|
|
GetItemName(szItemName);
|
|
|
|
// determine destination storage
|
|
ASSERT(m_lpStorage != NULL);
|
|
LPSTORAGE lpStorage = m_lpStorage;
|
|
if (!pDoc->m_bSameAsLoad)
|
|
{
|
|
// need to create new storage for this item
|
|
SCODE sc = pDoc->m_lpRootStg->CreateStorage(T2COLE(szItemName),
|
|
STGM_CREATE|STGM_READWRITE|STGM_TRANSACTED|STGM_SHARE_EXCLUSIVE,
|
|
0, 0, &lpStorage);
|
|
if (sc != S_OK)
|
|
{
|
|
TRACE1("Warning: unable to create child storage %s.\n",
|
|
szItemName);
|
|
AfxThrowOleException(sc);
|
|
}
|
|
// remember the storage for CommitItem stage
|
|
m_lpNewStorage = lpStorage;
|
|
m_bNeedCommit = TRUE;
|
|
}
|
|
ASSERT(lpStorage != NULL);
|
|
|
|
// save dirty object
|
|
LPPERSISTSTORAGE lpPersistStorage =
|
|
QUERYINTERFACE(m_lpObject, IPersistStorage);
|
|
ASSERT(lpPersistStorage != NULL);
|
|
SCODE sc = S_OK;
|
|
if (!pDoc->m_bSameAsLoad || lpPersistStorage->IsDirty() == S_OK)
|
|
{
|
|
// call OleSave now and IPersistStorage::SaveCompleted later
|
|
sc = ::OleSave(lpPersistStorage, lpStorage, pDoc->m_bSameAsLoad);
|
|
}
|
|
lpPersistStorage->Release();
|
|
|
|
// if it fails, abort the save
|
|
if (FAILED(sc))
|
|
AfxThrowOleException(sc);
|
|
|
|
// now will need to call CommitItem for this item
|
|
m_bNeedCommit = TRUE;
|
|
lpStorage->Commit(STGC_ONLYIFCURRENT);
|
|
}
|
|
|
|
void COleClientItem::GetItemStorage()
|
|
{
|
|
if (GetDocument()->m_bCompoundFile)
|
|
GetItemStorageCompound();
|
|
else
|
|
GetItemStorageFlat();
|
|
}
|
|
|
|
void COleClientItem::ReadItem(CArchive& ar)
|
|
{
|
|
if (GetDocument()->m_bCompoundFile)
|
|
ReadItemCompound(ar);
|
|
else
|
|
ReadItemFlat(ar);
|
|
}
|
|
|
|
void COleClientItem::WriteItem(CArchive& ar)
|
|
{
|
|
if (GetDocument()->m_bCompoundFile)
|
|
WriteItemCompound(ar);
|
|
else
|
|
WriteItemFlat(ar);
|
|
}
|
|
|
|
void COleClientItem::CommitItem(BOOL bSuccess)
|
|
{
|
|
ASSERT_VALID(this);
|
|
ASSERT(m_lpObject != NULL);
|
|
|
|
if (!m_bNeedCommit)
|
|
return;
|
|
|
|
LPPERSISTSTORAGE lpPersistStorage =
|
|
QUERYINTERFACE(m_lpObject, IPersistStorage);
|
|
ASSERT(lpPersistStorage != NULL);
|
|
|
|
// forget about new storage if save failed along the way...
|
|
if (m_lpNewStorage != NULL && !bSuccess)
|
|
RELEASE(m_lpNewStorage);
|
|
|
|
// let the object remember the new storage
|
|
VERIFY(lpPersistStorage->SaveCompleted(m_lpNewStorage) == S_OK);
|
|
lpPersistStorage->Release();
|
|
|
|
// determine/remember new storage
|
|
if (m_lpNewStorage != NULL)
|
|
{
|
|
m_lpStorage->Release();
|
|
m_lpStorage = m_lpNewStorage;
|
|
m_lpNewStorage = NULL;
|
|
}
|
|
|
|
m_bNeedCommit = FALSE;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// COleClientItem serialization
|
|
|
|
void COleClientItem::Serialize(CArchive& ar)
|
|
{
|
|
ASSERT_VALID(this);
|
|
|
|
CDocItem::Serialize(ar);
|
|
ASSERT(m_pDocument != NULL); // must 'SetDocument' first
|
|
|
|
if (ar.IsStoring())
|
|
{
|
|
ASSERT(m_lpObject != NULL);
|
|
|
|
// first, save the type flag (this is used for versioning)
|
|
ar << (DWORD)OT_OLE2;
|
|
|
|
ar << m_dwItemNumber; // save the item number
|
|
|
|
// write the view advise type to storage
|
|
ASSERT(m_lpViewObject != NULL);
|
|
DWORD dwAspect;
|
|
VERIFY(m_lpViewObject->GetAdvise(&dwAspect, NULL, NULL) == S_OK);
|
|
ar << dwAspect; // save the display aspect
|
|
|
|
// write flag indicating whether to create moniker upon load
|
|
ar << (WORD)m_bMoniker;
|
|
|
|
// save current default display aspect
|
|
ar << (DWORD)m_nDrawAspect;
|
|
|
|
// save object to storage (calls OleSave)
|
|
WriteItem(ar);
|
|
}
|
|
else
|
|
{
|
|
ASSERT(m_lpObject == NULL);
|
|
|
|
// first, get the type flag (for OLE 1.0 compatible reading)
|
|
DWORD dwType;
|
|
ar >> dwType;
|
|
if (dwType != OT_OLE2)
|
|
AfxThrowArchiveException(CArchiveException::generic);
|
|
|
|
ar >> m_dwItemNumber; // get the item number
|
|
|
|
DWORD dwAspect; // read the display aspect (aspects that are cached)
|
|
ar >> dwAspect;
|
|
|
|
WORD bMoniker; // see if we should create & set the moniker
|
|
ar >> bMoniker;
|
|
|
|
DWORD nDrawAspect; // read the default display aspect
|
|
ar >> nDrawAspect;
|
|
m_nDrawAspect = (DVASPECT)nDrawAspect;
|
|
|
|
// read the OLE object from storage (calls OleLoad)
|
|
ReadItem(ar);
|
|
|
|
// finish OLE object creation process, setup advises, etc.
|
|
if (!FinishCreate(S_OK))
|
|
AfxThrowArchiveException(CArchiveException::generic);
|
|
|
|
if (bMoniker)
|
|
{
|
|
// force moniker creation by calling GetMoniker
|
|
LPMONIKER lpMoniker;
|
|
if (GetClientSite()->GetMoniker(OLEGETMONIKER_FORCEASSIGN,
|
|
OLEWHICHMK_OBJREL, &lpMoniker) == S_OK)
|
|
{
|
|
ASSERT(lpMoniker != NULL);
|
|
lpMoniker->Release();
|
|
ASSERT(m_bMoniker); // moniker should have been assigned
|
|
}
|
|
}
|
|
|
|
// fix up the document's m_dwNextItemNumber
|
|
if (m_dwItemNumber >= GetDocument()->m_dwNextItemNumber)
|
|
GetDocument()->m_dwNextItemNumber = m_dwItemNumber + 1;
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// default callback implementation
|
|
|
|
void COleClientItem::OnChange(OLE_NOTIFICATION nCode, DWORD /*dwParam*/)
|
|
{
|
|
ASSERT_VALID(this);
|
|
|
|
switch (nCode)
|
|
{
|
|
case OLE_CLOSED:
|
|
break; // no default implementation
|
|
|
|
case OLE_CHANGED:
|
|
case OLE_SAVED:
|
|
ASSERT(m_pDocument != NULL);
|
|
m_pDocument->SetModifiedFlag();
|
|
break;
|
|
|
|
case OLE_CHANGED_STATE:
|
|
case OLE_CHANGED_ASPECT:
|
|
break; // no default implementation
|
|
|
|
default:
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
ASSERT_VALID(this);
|
|
}
|
|
|
|
void COleClientItem::OnDataChange(
|
|
LPFORMATETC /*lpFormatEtc*/, LPSTGMEDIUM /*lpStgMedium*/)
|
|
{
|
|
ASSERT(FALSE); // derivative must override -- must not call base class
|
|
}
|
|
|
|
void COleClientItem::OnGetItemPosition(CRect& /*rPosition*/)
|
|
{
|
|
// default does nothing
|
|
}
|
|
|
|
void COleClientItem::OnGetClipRect(CRect& rClipRect)
|
|
{
|
|
ASSERT_VALID(this);
|
|
ASSERT(AfxIsValidAddress(&rClipRect, sizeof(RECT)));
|
|
|
|
// default clips rectClip to client area of the active view
|
|
ASSERT_VALID(m_pView);
|
|
m_pView->GetClientRect(&rClipRect);
|
|
}
|
|
|
|
void COleClientItem::OnShowItem()
|
|
{
|
|
ASSERT_VALID(this);
|
|
|
|
CDocument* pDoc = GetDocument();
|
|
ASSERT_VALID(pDoc);
|
|
|
|
// attempt to use m_pView set during activation
|
|
CView* pView = m_pView;
|
|
if (pView == NULL)
|
|
{
|
|
// otherwise, find the first view of this document
|
|
POSITION pos = pDoc->GetFirstViewPosition();
|
|
if (pos == NULL || (pView = pDoc->GetNextView(pos)) == NULL)
|
|
return;
|
|
}
|
|
|
|
CFrameWnd* pFrameWnd = pView->GetParentFrame();
|
|
if (pFrameWnd != NULL)
|
|
{
|
|
// activate frame holding view
|
|
pFrameWnd->ActivateFrame();
|
|
pFrameWnd->OnUpdateFrameTitle(TRUE);
|
|
|
|
// activate app frame if necessary
|
|
pFrameWnd = pFrameWnd->GetParentFrame();
|
|
if (pFrameWnd != NULL)
|
|
{
|
|
ASSERT_KINDOF(CFrameWnd, pFrameWnd);
|
|
pFrameWnd->ActivateFrame();
|
|
pFrameWnd->OnUpdateFrameTitle(TRUE);
|
|
}
|
|
}
|
|
|
|
if (!pDoc->GetPathName().IsEmpty())
|
|
{
|
|
// user is also in control of the application, when a file-based
|
|
// document becomes visible.
|
|
AfxOleSetUserCtrl(TRUE);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// COleClientItem - attributes
|
|
|
|
void COleClientItem::GetClassID(CLSID* pClassID) const
|
|
{
|
|
ASSERT_VALID(this);
|
|
ASSERT(m_lpObject != NULL);
|
|
ASSERT(AfxIsValidAddress(pClassID, sizeof(CLSID)));
|
|
|
|
if (m_lpObject->GetUserClassID(pClassID) != S_OK)
|
|
*pClassID = CLSID_NULL;
|
|
}
|
|
|
|
BOOL COleClientItem::GetExtent(LPSIZE lpSize, DVASPECT nDrawAspect)
|
|
{
|
|
ASSERT_VALID(this);
|
|
ASSERT(m_lpObject != NULL);
|
|
ASSERT(AfxIsValidAddress(lpSize, sizeof(CSize)));
|
|
|
|
// use current default aspect if specific one not specified
|
|
if (nDrawAspect == -1)
|
|
nDrawAspect = m_nDrawAspect;
|
|
|
|
// get the extent
|
|
m_scLast = m_lpObject->GetExtent(nDrawAspect, lpSize);
|
|
return m_scLast == S_OK;
|
|
}
|
|
|
|
BOOL COleClientItem::GetCachedExtent(LPSIZE lpSize, DVASPECT nDrawAspect)
|
|
{
|
|
ASSERT_VALID(this);
|
|
ASSERT(m_lpViewObject != NULL);
|
|
ASSERT(AfxIsValidAddress(lpSize, sizeof(CSize)));
|
|
|
|
// use current default aspect if specific one not specified
|
|
if (nDrawAspect == -1)
|
|
nDrawAspect = m_nDrawAspect;
|
|
|
|
COleDocument* pDoc = (COleDocument*)GetDocument();
|
|
ASSERT_VALID(pDoc);
|
|
|
|
// get the extent
|
|
m_scLast = m_lpViewObject->GetExtent(nDrawAspect, -1, pDoc->m_ptd, lpSize);
|
|
return m_scLast == S_OK;
|
|
}
|
|
|
|
BOOL COleClientItem::SetIconicMetafile(HGLOBAL hMetaPict)
|
|
{
|
|
ASSERT_VALID(this);
|
|
ASSERT(m_lpObject != NULL);
|
|
|
|
// get IOleCache interface
|
|
LPOLECACHE lpOleCache = QUERYINTERFACE(m_lpObject, IOleCache);
|
|
if (lpOleCache == NULL)
|
|
{
|
|
TRACE0("Warning: object does not support IOleCache interface.\n");
|
|
return FALSE;
|
|
}
|
|
ASSERT(lpOleCache != NULL);
|
|
|
|
// new cache is for CF_METAFILEPICT, DVASPECT_ICON
|
|
FORMATETC formatEtc;
|
|
formatEtc.cfFormat = CF_METAFILEPICT;
|
|
formatEtc.ptd = NULL;
|
|
formatEtc.dwAspect = DVASPECT_ICON;
|
|
formatEtc.lindex = -1;
|
|
formatEtc.tymed = TYMED_MFPICT;
|
|
|
|
// setup the cache so iconic aspect is now included
|
|
DWORD dwConnection;
|
|
SCODE sc = lpOleCache->Cache(&formatEtc,
|
|
ADVF_NODATA|ADVF_PRIMEFIRST|ADVF_ONLYONCE, &dwConnection);
|
|
if (FAILED(sc))
|
|
{
|
|
lpOleCache->Release();
|
|
return FALSE;
|
|
}
|
|
|
|
// set data if iconic image provided
|
|
if (hMetaPict != NULL)
|
|
{
|
|
STGMEDIUM stgMedium;
|
|
stgMedium.tymed = TYMED_MFPICT;
|
|
stgMedium.hGlobal = hMetaPict;
|
|
stgMedium.pUnkForRelease = NULL;
|
|
|
|
sc = lpOleCache->SetData(&formatEtc, &stgMedium, FALSE);
|
|
if (FAILED(sc))
|
|
{
|
|
lpOleCache->Release();
|
|
return FALSE;
|
|
}
|
|
}
|
|
lpOleCache->Release();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
HGLOBAL COleClientItem::GetIconicMetafile()
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
ASSERT_VALID(this);
|
|
ASSERT(m_lpObject != NULL);
|
|
|
|
// get IDataObject interface
|
|
LPDATAOBJECT lpDataObject = QUERYINTERFACE(m_lpObject, IDataObject);
|
|
ASSERT(lpDataObject != NULL);
|
|
|
|
// cache is for CF_METAFILEPICT, DVASPECT_ICON
|
|
FORMATETC formatEtc;
|
|
formatEtc.cfFormat = CF_METAFILEPICT;
|
|
formatEtc.ptd = NULL;
|
|
formatEtc.dwAspect = DVASPECT_ICON;
|
|
formatEtc.lindex = -1;
|
|
formatEtc.tymed = TYMED_MFPICT;
|
|
|
|
// attempt to get the icon picture
|
|
STGMEDIUM stgMedium;
|
|
if (lpDataObject->GetData(&formatEtc, &stgMedium) != S_OK)
|
|
{
|
|
lpDataObject->Release();
|
|
|
|
// no current picture, attempt to get from class ID
|
|
CLSID clsid;
|
|
if (m_lpObject->GetUserClassID(&clsid) != S_OK)
|
|
return NULL;
|
|
|
|
TCHAR szTemp[_MAX_PATH];
|
|
LPTSTR lpszLabel = NULL;
|
|
if (GetType() == OT_LINK)
|
|
{
|
|
// it is a link, attempt to get link name
|
|
LPOLELINK lpOleLink = QUERYINTERFACE(m_lpObject, IOleLink);
|
|
if (lpOleLink != NULL)
|
|
{
|
|
LPOLESTR lpszDisplayName = NULL;
|
|
lpOleLink->GetSourceDisplayName(&lpszDisplayName);
|
|
if (lpszDisplayName != NULL)
|
|
{
|
|
szTemp[0] = 0;
|
|
AfxGetFileTitle(OLE2CT(lpszDisplayName), szTemp, _countof(szTemp));
|
|
if (szTemp[0] != '\0')
|
|
lpszLabel = szTemp;
|
|
CoTaskMemFree(lpszDisplayName);
|
|
}
|
|
lpOleLink->Release();
|
|
}
|
|
}
|
|
|
|
HGLOBAL hMetaPict = OleGetIconOfClass(clsid, T2OLE(lpszLabel), lpszLabel == NULL);
|
|
if (hMetaPict != NULL)
|
|
{
|
|
// cache it for later GetData (or drawing)
|
|
SetIconicMetafile(hMetaPict);
|
|
return hMetaPict;
|
|
}
|
|
return NULL;
|
|
}
|
|
lpDataObject->Release();
|
|
|
|
// can't handle data where punkForRelease is set
|
|
if (stgMedium.pUnkForRelease != NULL)
|
|
{
|
|
::ReleaseStgMedium(&stgMedium);
|
|
return NULL;
|
|
}
|
|
ASSERT(stgMedium.tymed == TYMED_MFPICT);
|
|
ASSERT(stgMedium.hGlobal != NULL);
|
|
|
|
return stgMedium.hGlobal; // return HGLOBAL to METAFILEPICT
|
|
}
|
|
|
|
void COleClientItem::SetDrawAspect(DVASPECT nDrawAspect)
|
|
{
|
|
ASSERT_VALID(this);
|
|
|
|
// prime iconic cache (in case object has never displayed iconic)
|
|
if (nDrawAspect == DVASPECT_ICON)
|
|
{
|
|
SetIconicMetafile(NULL); // allow object to provide own icon
|
|
HGLOBAL hMetaPict = GetIconicMetafile();
|
|
_AfxDeleteMetafilePict(hMetaPict);
|
|
}
|
|
|
|
// Note: the aspect you are setting may be uncached and therefore blank.
|
|
// To make sure it is cached use SetIconPicture or use IOleCache to
|
|
// set the cache yourself.
|
|
m_nDrawAspect = nDrawAspect;
|
|
|
|
// mark document as dirty (m_nDrawAspect is part of persistent state)
|
|
ASSERT_VALID(m_pDocument);
|
|
m_pDocument->SetModifiedFlag();
|
|
}
|
|
|
|
// Helper used to get the DVTARGETDEVICE pointer for the first printer.
|
|
BOOL COleClientItem::GetPrintDeviceInfo(
|
|
LPOLECACHE* plpOleCache, DVTARGETDEVICE** pptd, DWORD* pdwConnection)
|
|
{
|
|
*plpOleCache = NULL;
|
|
*pptd = NULL;
|
|
|
|
// get IOleCache interface
|
|
LPOLECACHE lpOleCache = QUERYINTERFACE(m_lpObject, IOleCache);
|
|
if (lpOleCache == NULL)
|
|
{
|
|
TRACE0("Warning: object does not support IOleCache interface.\n");
|
|
return FALSE; // no cache -- no possible print device
|
|
}
|
|
ASSERT(lpOleCache != NULL);
|
|
|
|
// get enumerator for the cache
|
|
LPENUMSTATDATA lpEnumSTATDATA;
|
|
if (lpOleCache->EnumCache(&lpEnumSTATDATA) != S_OK || lpEnumSTATDATA == NULL)
|
|
{
|
|
lpOleCache->Release();
|
|
return FALSE;
|
|
}
|
|
// enumerate entries in the cache (look for one with ptd != NULL)
|
|
STATDATA statData;
|
|
while (lpEnumSTATDATA->Next(1, &statData, NULL) == S_OK)
|
|
{
|
|
ASSERT(statData.pAdvSink == NULL);
|
|
|
|
// return first non-NULL ptd (we assume this is a printer cache)
|
|
if (statData.formatetc.ptd != NULL)
|
|
{
|
|
if (pdwConnection != NULL)
|
|
*pdwConnection = statData.dwConnection;
|
|
*pptd = statData.formatetc.ptd;
|
|
lpEnumSTATDATA->Release();
|
|
|
|
*plpOleCache = lpOleCache;
|
|
return TRUE; // Note: lpOleCache pointer is still alive
|
|
}
|
|
}
|
|
// release interfaces
|
|
lpEnumSTATDATA->Release();
|
|
lpOleCache->Release();
|
|
return FALSE; // data not found
|
|
}
|
|
|
|
void COleClientItem::AttachDataObject(COleDataObject& rDataObject) const
|
|
{
|
|
ASSERT_VALID(this);
|
|
ASSERT(m_lpObject != NULL);
|
|
|
|
// get the IDataObject interface for the item
|
|
LPDATAOBJECT lpDataObject = QUERYINTERFACE(m_lpObject, IDataObject);
|
|
ASSERT(lpDataObject != NULL);
|
|
|
|
// return it by attaching it
|
|
rDataObject.Attach(lpDataObject, TRUE);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// COleClientItem - general operations
|
|
|
|
BOOL COleClientItem::Draw(CDC* pDC, LPCRECT lpBounds, DVASPECT nDrawAspect)
|
|
{
|
|
ASSERT_VALID(this);
|
|
ASSERT(AfxIsValidAddress(lpBounds, sizeof(RECT), FALSE));
|
|
ASSERT_VALID(pDC);
|
|
|
|
if (m_lpObject == NULL || m_lpViewObject == NULL)
|
|
return FALSE; // partially created COleClientItem object
|
|
|
|
// use current draw aspect if aspect is -1 (default)
|
|
if (nDrawAspect == -1)
|
|
nDrawAspect = m_nDrawAspect;
|
|
|
|
// convert RECT lpBounds to RECTL rclBounds
|
|
RECTL rclBounds;
|
|
rclBounds.left = lpBounds->left;
|
|
rclBounds.top = lpBounds->top;
|
|
rclBounds.right = lpBounds->right;
|
|
rclBounds.bottom = lpBounds->bottom;
|
|
|
|
// get RECTL describing window extents and origin
|
|
RECTL rclWBounds;
|
|
CPoint ptOrg = pDC->GetWindowOrg();
|
|
CSize sizeExt = pDC->GetWindowExt();
|
|
rclWBounds.left = ptOrg.x;
|
|
rclWBounds.top = ptOrg.y;
|
|
rclWBounds.right = sizeExt.cx;
|
|
rclWBounds.bottom = sizeExt.cy;
|
|
|
|
// get target device to use for draw
|
|
COleDocument* pDoc = GetDocument();
|
|
const DVTARGETDEVICE* ptd = NULL;
|
|
HDC hdcTarget = NULL;
|
|
if (pDC->IsPrinting() && pDoc->m_ptd != NULL)
|
|
{
|
|
ptd = pDoc->m_ptd;
|
|
hdcTarget = pDC->m_hAttribDC;
|
|
}
|
|
|
|
// attempt draw with target device
|
|
SCODE sc = m_lpViewObject->Draw(nDrawAspect, -1, NULL,
|
|
(DVTARGETDEVICE*)ptd, hdcTarget, pDC->m_hDC,
|
|
&rclBounds, &rclWBounds, NULL, 0);
|
|
if (ptd != NULL && sc == OLE_E_BLANK)
|
|
{
|
|
// attempt draw without target device
|
|
sc = m_lpViewObject->Draw(nDrawAspect, -1, NULL,
|
|
NULL, NULL, pDC->m_hDC,
|
|
&rclBounds, &rclWBounds, NULL, 0);
|
|
}
|
|
|
|
if (sc != S_OK && sc == OLE_E_BLANK)
|
|
return FALSE; // return FALSE if the object is blank
|
|
|
|
CheckGeneral(sc); // otherwise, may throw exception on error
|
|
return TRUE;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// COleClientItem clipboard helpers
|
|
|
|
void COleClientItem::GetEmbeddedItemData(LPSTGMEDIUM lpStgMedium)
|
|
{
|
|
ASSERT_VALID(this);
|
|
ASSERT(AfxIsValidAddress(lpStgMedium, sizeof(STGMEDIUM)));
|
|
|
|
LPLOCKBYTES lpLockBytes;
|
|
SCODE sc = ::CreateILockBytesOnHGlobal(NULL, TRUE, &lpLockBytes);
|
|
if (sc != S_OK)
|
|
AfxThrowOleException(sc);
|
|
ASSERT(lpLockBytes != NULL);
|
|
|
|
LPSTORAGE lpStorage;
|
|
sc = ::StgCreateDocfileOnILockBytes(lpLockBytes,
|
|
STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_READWRITE, 0, &lpStorage);
|
|
if (sc != S_OK)
|
|
{
|
|
VERIFY(lpLockBytes->Release() == 0);
|
|
AfxThrowOleException(sc);
|
|
}
|
|
ASSERT(lpStorage != NULL);
|
|
lpLockBytes->Release();
|
|
|
|
// save the object into the storage
|
|
LPPERSISTSTORAGE lpPersistStorage =
|
|
QUERYINTERFACE(m_lpObject, IPersistStorage);
|
|
ASSERT(lpPersistStorage != NULL);
|
|
sc = ::OleSave(lpPersistStorage, lpStorage, FALSE);
|
|
lpPersistStorage->SaveCompleted(NULL);
|
|
lpPersistStorage->Release();
|
|
if (sc != S_OK)
|
|
{
|
|
VERIFY(lpStorage->Release() == 0);
|
|
AfxThrowOleException(sc);
|
|
}
|
|
|
|
// add it to the data source
|
|
lpStgMedium->tymed = TYMED_ISTORAGE;
|
|
lpStgMedium->pstg = lpStorage;
|
|
lpStgMedium->pUnkForRelease = NULL;
|
|
}
|
|
|
|
void COleClientItem::AddCachedData(COleDataSource* pDataSource)
|
|
{
|
|
ASSERT_VALID(this);
|
|
ASSERT_VALID(pDataSource);
|
|
|
|
// get IOleCache interface
|
|
LPOLECACHE lpOleCache = QUERYINTERFACE(m_lpObject, IOleCache);
|
|
if (lpOleCache == NULL)
|
|
{
|
|
TRACE0("Warning: object does not support IOleCache interface.\n");
|
|
return;
|
|
}
|
|
ASSERT(lpOleCache != NULL);
|
|
|
|
// Get IEnumSTATDATA interface for IOleCache
|
|
LPENUMSTATDATA lpEnumSTATDATA;
|
|
if (lpOleCache->EnumCache(&lpEnumSTATDATA) != S_OK || lpEnumSTATDATA == NULL)
|
|
{
|
|
lpOleCache->Release();
|
|
return;
|
|
}
|
|
|
|
// get IDataObject interface
|
|
LPDATAOBJECT lpDataObject = QUERYINTERFACE(m_lpObject, IDataObject);
|
|
ASSERT(lpDataObject != NULL);
|
|
|
|
// enumerate all of the cached formats
|
|
STATDATA statData;
|
|
while (lpEnumSTATDATA->Next(1, &statData, NULL) == S_OK)
|
|
{
|
|
ASSERT(statData.pAdvSink == NULL);
|
|
|
|
// for each format supported, attempt to get copy of the data
|
|
STGMEDIUM stgMedium;
|
|
if (lpDataObject->GetData(&statData.formatetc, &stgMedium) != S_OK)
|
|
{
|
|
// data is not available
|
|
CoTaskMemFree(statData.formatetc.ptd);
|
|
}
|
|
else if (stgMedium.pUnkForRelease != NULL)
|
|
{
|
|
// don't cache data with pUnkForRelease != NULL
|
|
::ReleaseStgMedium(&stgMedium);
|
|
CoTaskMemFree(statData.formatetc.ptd);
|
|
}
|
|
else
|
|
{
|
|
// format was acceptable -- add it to the clipboard
|
|
pDataSource->CacheData(0, &stgMedium, &statData.formatetc);
|
|
}
|
|
}
|
|
|
|
// release interfaces
|
|
lpEnumSTATDATA->Release();
|
|
lpDataObject->Release();
|
|
lpOleCache->Release();
|
|
}
|
|
|
|
BOOL COleClientItem::GetLinkSourceData(LPSTGMEDIUM lpStgMedium)
|
|
{
|
|
ASSERT_VALID(this);
|
|
ASSERT(AfxIsValidAddress(lpStgMedium, sizeof(STGMEDIUM)));
|
|
|
|
LPMONIKER lpMoniker = NULL;
|
|
LPOLELINK lpOleLink = QUERYINTERFACE(m_lpObject, IOleLink);
|
|
if (lpOleLink == NULL)
|
|
{
|
|
// get moniker from client site
|
|
LPOLECLIENTSITE lpClientSite = GetClientSite();
|
|
ASSERT(lpClientSite != NULL);
|
|
SCODE sc = lpClientSite->GetMoniker(OLEGETMONIKER_TEMPFORUSER,
|
|
OLEWHICHMK_OBJFULL, &lpMoniker);
|
|
if (sc != S_OK)
|
|
{
|
|
TRACE0("Warning: unable to get moniker from client site.\n");
|
|
return FALSE;
|
|
}
|
|
ASSERT(lpMoniker != NULL);
|
|
}
|
|
else
|
|
{
|
|
// get moniker from the link object itself
|
|
SCODE sc = lpOleLink->GetSourceMoniker(&lpMoniker);
|
|
lpOleLink->Release();
|
|
if (sc != S_OK)
|
|
{
|
|
TRACE0("Warning: unable to get moniker from link source.\n");
|
|
return FALSE;
|
|
}
|
|
ASSERT(lpMoniker != NULL);
|
|
}
|
|
|
|
// create a memory based stream to write the moniker to
|
|
LPSTREAM lpStream;
|
|
if (::CreateStreamOnHGlobal(NULL, TRUE, &lpStream) != S_OK)
|
|
{
|
|
lpMoniker->Release();
|
|
AfxThrowMemoryException();
|
|
}
|
|
ASSERT(lpStream != NULL);
|
|
|
|
// write the moniker to the stream, and add it to the clipboard
|
|
SCODE sc = ::OleSaveToStream(lpMoniker, lpStream);
|
|
lpMoniker->Release();
|
|
if (sc != S_OK)
|
|
{
|
|
lpStream->Release();
|
|
AfxThrowOleException(sc);
|
|
}
|
|
|
|
// write the class ID of the document to the stream as well
|
|
CLSID clsid;
|
|
sc = m_lpObject->GetUserClassID(&clsid);
|
|
if (sc != S_OK)
|
|
{
|
|
lpStream->Release();
|
|
AfxThrowOleException(sc);
|
|
}
|
|
sc = WriteClassStm(lpStream, clsid);
|
|
if (sc != S_OK)
|
|
{
|
|
lpStream->Release();
|
|
AfxThrowOleException(sc);
|
|
}
|
|
|
|
// add it to the data source
|
|
lpStgMedium->tymed = TYMED_ISTREAM;
|
|
lpStgMedium->pstm = lpStream;
|
|
lpStgMedium->pUnkForRelease = NULL;
|
|
return TRUE;
|
|
}
|
|
|
|
void COleClientItem::GetObjectDescriptorData(
|
|
LPPOINT lpOffset, LPSIZE lpSize, LPSTGMEDIUM lpStgMedium)
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
ASSERT_VALID(this);
|
|
ASSERT(AfxIsValidAddress(lpStgMedium, sizeof(STGMEDIUM)));
|
|
ASSERT(lpOffset == NULL ||
|
|
AfxIsValidAddress(lpOffset, sizeof(CPoint), FALSE));
|
|
|
|
POINTL pointT;
|
|
if (lpOffset != NULL)
|
|
{
|
|
pointT.x = lpOffset->x;
|
|
pointT.y = lpOffset->y;
|
|
#ifndef _MAC
|
|
((CDC*)NULL)->DPtoHIMETRIC((SIZE*)&pointT);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
pointT.x = 0;
|
|
pointT.y = 0;
|
|
}
|
|
|
|
SIZE sizeT;
|
|
if (lpSize != NULL)
|
|
{
|
|
sizeT.cx = lpSize->cx;
|
|
sizeT.cy = lpSize->cy;
|
|
#ifndef _MAC
|
|
((CDC*)NULL)->DPtoHIMETRIC(&sizeT);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
sizeT.cx = 0;
|
|
sizeT.cy = 0;
|
|
}
|
|
|
|
COleDocument* pDoc = GetDocument();
|
|
|
|
// get the object descriptor for the IOleObject
|
|
InterlockedIncrement(&m_dwRef);
|
|
HGLOBAL hGlobal = _AfxOleGetObjectDescriptorData(
|
|
m_lpObject, T2COLE(pDoc->GetPathName()), m_nDrawAspect, pointT, &sizeT);
|
|
InterlockedDecrement(&m_dwRef);
|
|
|
|
if (hGlobal == NULL)
|
|
AfxThrowMemoryException();
|
|
|
|
// setup the STGMEDIUM
|
|
lpStgMedium->tymed = TYMED_HGLOBAL;
|
|
lpStgMedium->hGlobal = hGlobal;
|
|
lpStgMedium->pUnkForRelease = NULL;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Embedded COleClientItem operations
|
|
|
|
void COleClientItem::SetHostNames(LPCTSTR lpszHost, LPCTSTR lpszHostObj)
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
ASSERT_VALID(this);
|
|
ASSERT(m_lpObject != NULL);
|
|
ASSERT(AfxIsValidString(lpszHost));
|
|
|
|
ASSERT(AfxIsValidString(lpszHostObj));
|
|
CheckGeneral(m_lpObject->SetHostNames(T2COLE(lpszHost),
|
|
T2COLE(lpszHostObj)));
|
|
}
|
|
|
|
void COleClientItem::SetExtent(const CSize& size, DVASPECT nDrawAspect)
|
|
{
|
|
ASSERT_VALID(this);
|
|
ASSERT(m_lpObject != NULL);
|
|
|
|
CheckGeneral(m_lpObject->SetExtent(nDrawAspect, (SIZE*)&size));
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// COleClientItem OLE interface implementation
|
|
|
|
BEGIN_INTERFACE_MAP(COleClientItem, CDocItem)
|
|
INTERFACE_PART(COleClientItem, IID_IOleClientSite, OleClientSite)
|
|
INTERFACE_PART(COleClientItem, IID_IAdviseSink, AdviseSink)
|
|
INTERFACE_PART(COleClientItem, IID_IOleWindow, OleIPSite)
|
|
INTERFACE_PART(COleClientItem, IID_IOleInPlaceSite, OleIPSite)
|
|
END_INTERFACE_MAP()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Implementation of IOleClientSite
|
|
|
|
STDMETHODIMP_(ULONG) COleClientItem::XOleClientSite::AddRef()
|
|
{
|
|
METHOD_PROLOGUE_EX_(COleClientItem, OleClientSite)
|
|
return pThis->ExternalAddRef();
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) COleClientItem::XOleClientSite::Release()
|
|
{
|
|
METHOD_PROLOGUE_EX_(COleClientItem, OleClientSite)
|
|
return pThis->ExternalRelease();
|
|
}
|
|
|
|
STDMETHODIMP COleClientItem::XOleClientSite::QueryInterface(
|
|
REFIID iid, LPVOID* ppvObj)
|
|
{
|
|
METHOD_PROLOGUE_EX_(COleClientItem, OleClientSite)
|
|
return pThis->ExternalQueryInterface(&iid, ppvObj);
|
|
}
|
|
|
|
STDMETHODIMP COleClientItem::XOleClientSite::SaveObject()
|
|
{
|
|
METHOD_PROLOGUE_EX(COleClientItem, OleClientSite)
|
|
ASSERT_VALID(pThis);
|
|
|
|
LPPERSISTSTORAGE lpPersistStorage =
|
|
QUERYINTERFACE(pThis->m_lpObject, IPersistStorage);
|
|
ASSERT(lpPersistStorage != NULL);
|
|
SCODE sc = S_OK;
|
|
if (lpPersistStorage->IsDirty() == S_OK)
|
|
{
|
|
// S_OK == S_OK != S_FALSE, therefore object is dirty!
|
|
sc = ::OleSave(lpPersistStorage, pThis->m_lpStorage, TRUE);
|
|
if (sc != S_OK)
|
|
sc = lpPersistStorage->SaveCompleted(NULL);
|
|
|
|
// mark the document as dirty, if save sucessful.
|
|
pThis->m_pDocument->SetModifiedFlag();
|
|
}
|
|
lpPersistStorage->Release();
|
|
return sc;
|
|
}
|
|
|
|
STDMETHODIMP COleClientItem::XOleClientSite::GetMoniker(
|
|
DWORD dwAssign, DWORD dwWhichMoniker, LPMONIKER* ppMoniker)
|
|
{
|
|
METHOD_PROLOGUE_EX(COleClientItem, OleClientSite)
|
|
ASSERT_VALID(pThis);
|
|
|
|
USES_CONVERSION;
|
|
|
|
COleDocument* pDoc = pThis->GetDocument();
|
|
ASSERT_VALID(pDoc);
|
|
ASSERT(ppMoniker != NULL);
|
|
*ppMoniker = NULL;
|
|
|
|
switch (dwWhichMoniker)
|
|
{
|
|
case OLEWHICHMK_CONTAINER:
|
|
// return the current moniker for the document
|
|
*ppMoniker = pDoc->GetMoniker((OLEGETMONIKER)dwAssign);
|
|
break;
|
|
|
|
case OLEWHICHMK_OBJREL:
|
|
{
|
|
if (!pDoc->IsKindOf(RUNTIME_CLASS(COleLinkingDoc)))
|
|
break;
|
|
|
|
// don't return relative moniker if no document moniker
|
|
LPMONIKER lpMoniker = pDoc->GetMoniker((OLEGETMONIKER)dwAssign);
|
|
if (lpMoniker == NULL)
|
|
break;
|
|
lpMoniker->Release();
|
|
|
|
// relative monikers have to handle assignment correctly
|
|
switch (dwAssign)
|
|
{
|
|
case OLEGETMONIKER_ONLYIFTHERE:
|
|
if (!pThis->m_bMoniker)
|
|
break; // no moniker assigned, don't return one
|
|
// fall through...
|
|
|
|
case OLEGETMONIKER_TEMPFORUSER:
|
|
case OLEGETMONIKER_FORCEASSIGN:
|
|
{
|
|
// create item moniker from item name
|
|
TCHAR szItemName[OLE_MAXITEMNAME];
|
|
pThis->GetItemName(szItemName);
|
|
CreateItemMoniker(OLESTDDELIMOLE, T2COLE(szItemName),
|
|
ppMoniker);
|
|
|
|
// notify the object of the assignment
|
|
if (dwAssign != OLEGETMONIKER_TEMPFORUSER &&
|
|
*ppMoniker != NULL && !pThis->m_bMoniker)
|
|
{
|
|
pThis->m_bMoniker = TRUE;
|
|
VERIFY(pThis->m_lpObject->SetMoniker(
|
|
OLEWHICHMK_OBJREL, *ppMoniker) == S_OK);
|
|
ASSERT_VALID(pThis->m_pDocument);
|
|
pThis->m_pDocument->SetModifiedFlag();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case OLEGETMONIKER_UNASSIGN:
|
|
pThis->m_bMoniker = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case OLEWHICHMK_OBJFULL:
|
|
{
|
|
// get each sub-moniker: item & document
|
|
LPMONIKER lpMoniker1, lpMoniker2;
|
|
GetMoniker(dwAssign, OLEWHICHMK_CONTAINER, &lpMoniker1);
|
|
GetMoniker(dwAssign, OLEWHICHMK_OBJREL, &lpMoniker2);
|
|
|
|
// create composite moniker
|
|
if (lpMoniker1 != NULL && lpMoniker2 != NULL)
|
|
::CreateGenericComposite(lpMoniker1, lpMoniker2, ppMoniker);
|
|
|
|
// release sub-monikers
|
|
RELEASE(lpMoniker1);
|
|
RELEASE(lpMoniker2);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return *ppMoniker != NULL ? S_OK : E_FAIL;
|
|
}
|
|
|
|
STDMETHODIMP COleClientItem::XOleClientSite::GetContainer(
|
|
LPOLECONTAINER* ppContainer)
|
|
{
|
|
#ifdef _DEBUG
|
|
METHOD_PROLOGUE_EX(COleClientItem, OleClientSite)
|
|
#else
|
|
METHOD_PROLOGUE_EX_(COleClientItem, OleClientSite)
|
|
#endif
|
|
|
|
// return the IOleItemContainer interface in the document
|
|
COleDocument* pDoc = pThis->GetDocument();
|
|
ASSERT_VALID(pDoc);
|
|
*ppContainer = pDoc->GetContainer();
|
|
|
|
return *ppContainer != NULL ? S_OK : E_NOINTERFACE;
|
|
}
|
|
|
|
STDMETHODIMP COleClientItem::XOleClientSite::ShowObject()
|
|
{
|
|
METHOD_PROLOGUE_EX(COleClientItem, OleClientSite)
|
|
ASSERT_VALID(pThis);
|
|
|
|
TRY
|
|
{
|
|
pThis->OnShowItem();
|
|
}
|
|
END_TRY
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP COleClientItem::XOleClientSite::OnShowWindow(BOOL fShow)
|
|
{
|
|
METHOD_PROLOGUE_EX(COleClientItem, OleClientSite)
|
|
ASSERT_VALID(pThis);
|
|
|
|
// ignore this if the item is already in-place
|
|
if (pThis->IsInPlaceActive())
|
|
return S_OK;
|
|
|
|
TRY
|
|
{
|
|
// store new state of object -- determines how object may be drawn
|
|
COleClientItem::ItemState uNewState;
|
|
uNewState = fShow ? COleClientItem::openState :
|
|
COleClientItem::loadedState;
|
|
if (uNewState != pThis->m_nItemState)
|
|
{
|
|
pThis->OnChange(OLE_CHANGED_STATE, (DWORD)uNewState);
|
|
pThis->m_nItemState = uNewState;
|
|
}
|
|
}
|
|
END_TRY
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP COleClientItem::XOleClientSite::RequestNewObjectLayout()
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Implementation of IAdviseSink
|
|
|
|
STDMETHODIMP_(ULONG) COleClientItem::XAdviseSink::AddRef()
|
|
{
|
|
METHOD_PROLOGUE_EX_(COleClientItem, AdviseSink)
|
|
return pThis->ExternalAddRef();
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) COleClientItem::XAdviseSink::Release()
|
|
{
|
|
METHOD_PROLOGUE_EX_(COleClientItem, AdviseSink)
|
|
return pThis->ExternalRelease();
|
|
}
|
|
|
|
STDMETHODIMP COleClientItem::XAdviseSink::QueryInterface(
|
|
REFIID iid, LPVOID* ppvObj)
|
|
{
|
|
METHOD_PROLOGUE_EX_(COleClientItem, AdviseSink)
|
|
return pThis->ExternalQueryInterface(&iid, ppvObj);
|
|
}
|
|
|
|
STDMETHODIMP_(void) COleClientItem::XAdviseSink::OnDataChange(
|
|
LPFORMATETC lpFormatEtc, LPSTGMEDIUM lpStgMedium)
|
|
{
|
|
METHOD_PROLOGUE_EX(COleClientItem, AdviseSink)
|
|
ASSERT_VALID(pThis);
|
|
|
|
// Only interesting for advanced containers. Forward it such that
|
|
// containers do not have to implement the entire interface.
|
|
pThis->OnDataChange(lpFormatEtc, lpStgMedium);
|
|
}
|
|
|
|
STDMETHODIMP_(void) COleClientItem::XAdviseSink::OnViewChange(
|
|
DWORD aspects, LONG /*lindex*/)
|
|
{
|
|
METHOD_PROLOGUE_EX(COleClientItem, AdviseSink)
|
|
ASSERT_VALID(pThis);
|
|
|
|
pThis->OnChange(OLE_CHANGED, (DVASPECT)aspects);
|
|
}
|
|
|
|
STDMETHODIMP_(void) COleClientItem::XAdviseSink::OnRename(
|
|
LPMONIKER /*lpMoniker*/)
|
|
{
|
|
// only interesting to the OLE link object. Containers ignore this.
|
|
}
|
|
|
|
STDMETHODIMP_(void) COleClientItem::XAdviseSink::OnSave()
|
|
{
|
|
METHOD_PROLOGUE_EX(COleClientItem, AdviseSink)
|
|
ASSERT_VALID(pThis);
|
|
|
|
pThis->OnChange(OLE_SAVED, (DVASPECT)0);
|
|
}
|
|
|
|
STDMETHODIMP_(void) COleClientItem::XAdviseSink::OnClose()
|
|
{
|
|
METHOD_PROLOGUE_EX(COleClientItem, AdviseSink)
|
|
ASSERT_VALID(pThis);
|
|
|
|
pThis->OnChange(OLE_CLOSED, (DVASPECT)0);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// COleClientItem diagnostics
|
|
|
|
#ifdef _DEBUG
|
|
void COleClientItem::AssertValid() const
|
|
{
|
|
CDocItem::AssertValid();
|
|
if (m_lpNewStorage != NULL)
|
|
ASSERT(m_bNeedCommit);
|
|
if (m_pView != NULL)
|
|
m_pView->AssertValid();
|
|
if (m_pInPlaceFrame != NULL)
|
|
m_pInPlaceFrame->AssertValid();
|
|
if (m_pInPlaceDoc != NULL)
|
|
m_pInPlaceDoc->AssertValid();
|
|
}
|
|
|
|
void COleClientItem::Dump(CDumpContext& dc) const
|
|
{
|
|
CDocItem::Dump(dc);
|
|
|
|
// shallow dump
|
|
dc << "m_lpObject = " << (void*)m_lpObject;
|
|
dc << "\nm_dwItemNumber = " << m_dwItemNumber;
|
|
dc << "\nm_nDrawAspect = " << (int)m_nDrawAspect;
|
|
dc << "\nm_scLast = " << m_scLast;
|
|
dc << "\nm_lpStorage = " << m_lpStorage;
|
|
dc << "\nm_lpLockBytes = " << m_lpLockBytes;
|
|
dc << "\nm_dwConnection = " << m_dwConnection;
|
|
dc << "\nm_bLinkUnavail = " << m_bLinkUnavail;
|
|
dc << "\nm_bMoniker = " << m_bMoniker;
|
|
dc << "\nm_lpNewStorage = " << m_lpNewStorage;
|
|
dc << "\nm_bNeedCommit = " << m_bNeedCommit;
|
|
dc << "\nm_nItemState = " << (int)m_nItemState;
|
|
dc << "\nm_pView = " << (void*)m_pView;
|
|
dc << "\nm_dwContainerStyle = " << m_dwContainerStyle;
|
|
dc << "\nm_pInPlaceFrame = " << (void*)m_pInPlaceFrame;
|
|
dc << "\nm_hWndServer = " << m_hWndServer;
|
|
}
|
|
#endif //_DEBUG
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Inline function declarations expanded out-of-line
|
|
|
|
#ifndef _AFX_ENABLE_INLINES
|
|
|
|
// expand inlines for OLE client APIs
|
|
static char _szAfxOleInl[] = "afxole.inl";
|
|
#undef THIS_FILE
|
|
#define THIS_FILE _szAfxOleInl
|
|
#define _AFXOLECLI_INLINE
|
|
#define _AFXOLEDOBJ_INLINE
|
|
#include "afxole.inl"
|
|
|
|
#endif //!_AFX_ENABLE_INLINES
|
|
|
|
#ifdef AFX_INIT_SEG
|
|
#pragma code_seg(AFX_INIT_SEG)
|
|
#endif
|
|
|
|
// IMPLEMENT_DYNAMIC for COleLinkingDoc here for better .OBJ granularity
|
|
IMPLEMENT_DYNAMIC(COleLinkingDoc, COleDocument)
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|