// 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_OLE3_SEG
#pragma code_seg(AFX_OLE3_SEG)
#endif

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define new DEBUG_NEW

#define OLE_MAXNAMESIZE     (256)

/////////////////////////////////////////////////////////////////////////////
// COleLinkingDoc - enables linking to embeddings (basis for server)

COleLinkingDoc::COleLinkingDoc()
{
	m_dwRegister = 0;
	m_pFactory = NULL;
	m_bVisibleLock = FALSE;
	m_bDeferErrors = FALSE;
	m_pLastException = NULL;
	m_lpMonikerROT = NULL;

	ASSERT_VALID(this);
}

COleLinkingDoc::~COleLinkingDoc()
{
	ASSERT_VALID(this);

	ASSERT(!m_bVisibleLock);

	DisconnectViews();
	ASSERT(m_viewList.IsEmpty());

	Revoke();   // cleanup naming support

	ExternalDisconnect();
}

/////////////////////////////////////////////////////////////////////////////
// COleLinkingDoc moniker handling

LPMONIKER COleLinkingDoc::GetMoniker(OLEGETMONIKER nAssign)
{
	USES_CONVERSION;

	ASSERT_VALID(this);

	// use base class implementation if no registered moniker
	if (m_strMoniker.IsEmpty())
		return COleDocument::GetMoniker(nAssign);

	// return file moniker based on current path name
	LPMONIKER lpMoniker;
	CreateFileMoniker(T2COLE(m_strMoniker), &lpMoniker);
	return lpMoniker;
}

BOOL COleLinkingDoc::Register(COleObjectFactory* pFactory, LPCTSTR lpszPathName)
{
	USES_CONVERSION;

	ASSERT_VALID(this);
	ASSERT(pFactory == NULL ||
		AfxIsValidAddress(pFactory, sizeof(COleObjectFactory)));
	ASSERT(lpszPathName == NULL || AfxIsValidString(lpszPathName));
	ASSERT(m_dwRegister == 0);

	// attach the document to the server
	ASSERT(m_pFactory == NULL || m_pFactory == pFactory);
	m_pFactory = pFactory;

	BOOL bResult = TRUE;

	// create file moniker based on path name
	RELEASE(m_lpMonikerROT);
	m_strMoniker.Empty();
	if (lpszPathName != NULL)
	{
		if (CreateFileMoniker(T2COLE(lpszPathName), &m_lpMonikerROT) != S_OK)
			bResult = FALSE;
	}

	// register file moniker as running
	if (m_lpMonikerROT != NULL)
	{
		// see if the object is already running in the ROT
		LPRUNNINGOBJECTTABLE lpROT = NULL;
		VERIFY(GetRunningObjectTable(0, &lpROT) == S_OK);
		ASSERT(lpROT != NULL);
		LPUNKNOWN lpUnk;
		if (lpROT->GetObject(m_lpMonikerROT, &lpUnk) == S_OK)
		{
			// fatal error -- can't register same moniker twice!
			lpUnk->Release();
			RELEASE(m_lpMonikerROT);
			return FALSE;
		}
		// not already running -- so ok to attempt registration
		SCODE sc = lpROT->Register(NULL, (LPUNKNOWN)
			GetInterface(&IID_IUnknown), m_lpMonikerROT, &m_dwRegister);
		lpROT->Release();
		m_strMoniker = lpszPathName;
		if (sc != S_OK)
			bResult = FALSE;
	}

	// update all objects with new moniker
	POSITION pos = GetStartPosition();
	COleClientItem* pItem;
	while ((pItem = GetNextClientItem(pos)) != NULL)
	{
		if (pItem->m_bMoniker)
		{
			ASSERT(pItem->m_lpObject != NULL);
			pItem->m_lpObject->SetMoniker(OLEWHICHMK_CONTAINER,
				m_lpMonikerROT);
		}
	}

	return bResult;
}

void COleLinkingDoc::Revoke()
{
	ASSERT_VALID(this);

	// revoke current registration
	if (m_dwRegister != 0)
	{
		LPRUNNINGOBJECTTABLE lpROT = NULL;
		GetRunningObjectTable(0, &lpROT);
		if (lpROT != NULL)
		{
			lpROT->Revoke(m_dwRegister);
			lpROT->Release();
		}
		m_dwRegister = 0;
	}
	RELEASE(m_lpMonikerROT);
	m_strMoniker = _T("");
}

BOOL COleLinkingDoc::OnNewDocument()
{
	ASSERT_VALID(this);

	Revoke();
	RegisterIfServerAttached(NULL, TRUE);

	if (!COleDocument::OnNewDocument())
		return FALSE;

	AfxOleSetUserCtrl(TRUE);

	return TRUE;
}

BOOL COleLinkingDoc::OnOpenDocument(LPCTSTR lpszPathName)
{
	ASSERT_VALID(this);

	// always register the document before opening it
	Revoke();
	if (!RegisterIfServerAttached(lpszPathName, FALSE))
	{
		// always output a trace (it is just an FYI -- not generally fatal)
		TRACE1("Warning: Unable to register moniker '%s' as running\n", lpszPathName);
	}

	if (!COleDocument::OnOpenDocument(lpszPathName))
	{
		Revoke();
		return FALSE;
	}

	// if the app was started only to print, don't set user control

	CWinApp* pApp = AfxGetApp();
	ASSERT(pApp != NULL);
	if (pApp->m_pCmdInfo == NULL ||
		(pApp->m_pCmdInfo->m_nShellCommand != CCommandLineInfo::FileDDE &&
		 pApp->m_pCmdInfo->m_nShellCommand != CCommandLineInfo::FilePrint))
	{
		AfxOleSetUserCtrl(TRUE);
	}

	return TRUE;
}

BOOL COleLinkingDoc::OnSaveDocument(LPCTSTR lpszPathName)
{
	ASSERT_VALID(this);

	BOOL bRemember = m_bRemember;
	if (!COleDocument::OnSaveDocument(lpszPathName))
		return FALSE;

	if (bRemember && (m_strMoniker != lpszPathName))
	{
		// update the moniker/registration since the name has changed
		Revoke();
		RegisterIfServerAttached(lpszPathName, TRUE);
	}
	return TRUE;
}

void COleLinkingDoc::OnCloseDocument()
{
	InternalAddRef();   // protect document during shutdown

	// update lock count before sending notifications
	UpdateVisibleLock(FALSE, FALSE);

	Revoke();   // cleanup naming support

	// remove visible lock if present
	if (m_bVisibleLock)
	{
		m_bVisibleLock = FALSE;
		LockExternal(FALSE, FALSE);
	}

	// cleanup the document but don't delete yet
	BOOL bAutoDelete = m_bAutoDelete;
	m_bAutoDelete = FALSE;
	COleDocument::OnCloseDocument();
	ASSERT_VALID(this);

	// remove extra reference count and destroy
	InterlockedDecrement(&m_dwRef);
	if (bAutoDelete)
		delete this;    // now safe to destroy document
}

void COleLinkingDoc::UpdateVisibleLock(BOOL bVisible, BOOL bRemoveRefs)
{
	ASSERT_VALID(this);

	if (bVisible != m_bVisibleLock)
	{
		InternalAddRef();   // make sure document is stable
		m_bVisibleLock = bVisible;
		LockExternal(bVisible, bRemoveRefs);
		InternalRelease();  // may Release the document!
	}
}

void COleLinkingDoc::OnShowViews(BOOL bVisible)
{
	if (bVisible)
		UpdateVisibleLock(bVisible, TRUE);
}

void COleLinkingDoc::SaveToStorage(CObject* pObject)
{
	ASSERT_VALID(this);
	if (pObject != NULL)
		ASSERT_VALID(pObject);

	// write the classID of the application to the root storage
	if (m_pFactory != NULL)
	{
		ASSERT(m_lpRootStg != NULL);
		WriteClassStg(m_lpRootStg, m_pFactory->GetClassID());
	}
	COleDocument::SaveToStorage(pObject);
}

BOOL COleLinkingDoc::RegisterIfServerAttached(LPCTSTR lpszPathName, BOOL bMessage)
{
	ASSERT_VALID(this);
	ASSERT(lpszPathName == NULL || AfxIsValidString(lpszPathName));

	CDocTemplate* pTemplate = GetDocTemplate();
	ASSERT_VALID(pTemplate);

	COleObjectFactory* pFactory =
		(COleObjectFactory*)pTemplate->m_pAttachedFactory;
	if (pFactory != NULL)
	{
		// always attach the document to the server at this time
		ASSERT_KINDOF(COleObjectFactory, pFactory);
		m_pFactory = pFactory;

		// register with OLE Server
		if (!Register(pFactory, lpszPathName))
		{
			if (bMessage)
			{
				// only report error when message box allowed
				ReportSaveLoadException(lpszPathName, NULL, FALSE,
					AFX_IDP_FAILED_TO_NOTIFY);
			}
			return FALSE;
		}
	}
	return TRUE;
}

LPOLEITEMCONTAINER COleLinkingDoc::GetContainer()
{
	ASSERT_VALID(this);

	// get the IOleItemContainer interface via QueryInterface
	LPOLEITEMCONTAINER lpContainer;
	InternalQueryInterface(&IID_IOleItemContainer, (LPLP)&lpContainer);
	return lpContainer;
}

/////////////////////////////////////////////////////////////////////////////
// COleLinkingDoc default implementation

COleServerItem* COleLinkingDoc::OnGetLinkedItem(LPCTSTR /*lpszItemName*/)
{
	ASSERT_VALID(this);

	// default implementation is in COleServerDoc
	return NULL;
}

COleClientItem* COleLinkingDoc::OnFindEmbeddedItem(LPCTSTR lpszItemName)
{
	ASSERT_VALID(this);
	ASSERT(AfxIsValidString(lpszItemName));

	// default implementation walks list of client items looking for
	//  a case sensitive match

	POSITION pos = GetStartPosition();
	COleClientItem* pItem;
	while ((pItem = GetNextClientItem(pos)) != NULL)
	{
		// a client item is running if there is a match in name
		//  and the m_lpObject is also running.
		TCHAR szItemName[OLE_MAXITEMNAME];
		pItem->GetItemName(szItemName);
		if (lstrcmp(szItemName, lpszItemName) == 0)
			return pItem;
	}
#ifdef _DEBUG
	if (afxTraceFlags & traceOle)
	{
		TRACE0("Warning: default COleLinkingDoc::OnFindEmbeddedItem\n");
		TRACE1("\timplementation failed to find item '%s'.\n", lpszItemName);
	}
#endif
	return NULL;    // no matching item found
}

void COleLinkingDoc::LockExternal(BOOL bLock, BOOL bRemoveRefs)
{
	// when an item binding is successful, the original document
	//  is released.  To keep it alive and the RPC stubs that make
	//  it available to the external world (via the running object
	//  table), we need to place a lock on it.

	// a lock created with CoLockObjectExternal adds a reference
	//  to the object itself (with IUnknown::AddRef) as well
	//  as keeping the RPC stub alive.

	::CoLockObjectExternal((LPUNKNOWN)GetInterface(&IID_IUnknown),
		bLock, bRemoveRefs);

	if (bLock)
	{
		// avoid "dead" objects in the running object table (ROT), by
		//  re-registering this object in the ROT.
		if (!m_strPathName.IsEmpty())
		{
			Revoke();
			RegisterIfServerAttached(m_strPathName, FALSE);
		}
	}
}

void COleLinkingDoc::ReportSaveLoadException(LPCTSTR lpszPathName,
	CException* e, BOOL bSaving, UINT nIDPDefault)
{
	// watch out for special mode
	if (m_bDeferErrors)
	{
		// Note: CException::Delete does not treat m_bAutoDelete as a
		// traditional  BOOL. Only if it is greater than zero does it
		// take on a TRUE quality.  (that is, all tests are for
		// m_bAutoDelete > 0).  So, if m_bAutoDelete is already "true"
		// (1) this will make it false, and if it is already "false"
		//  it is still considered "false".  Valid values for
		// m_bAutoDelete are thus negative, 0, and 1.  Values greater
		// than 1, although not explicitly asserted in CException,
		// would be invalid.  In short, by using increment  and
		// decrement operations, we enable this to work with both
		// self-deleting and non-self-deleting CException classes.

		--e->m_bAutoDelete;

		// save the exception for later
		m_pLastException = e;
		return;
	}

	// otherwise, just call base class
	COleDocument::ReportSaveLoadException(lpszPathName, e, bSaving,
		nIDPDefault);
}

SCODE COleLinkingDoc::EndDeferErrors(SCODE sc)
{
	ASSERT(m_bDeferErrors != 0);
	--m_bDeferErrors;
	if (m_pLastException != NULL)
	{
		ASSERT_VALID(m_pLastException);
		if (sc == S_OK)
			sc = COleException::Process(m_pLastException);

		// Note: See note above in ReportSaveLoadException for
		// a comment regarding the special treatment of m_bAutoDelete.

		++m_pLastException->m_bAutoDelete;

		// now get rid of the exception that we saved
		m_pLastException->Delete();
		m_pLastException = NULL;
	}
	return sc;
}

/////////////////////////////////////////////////////////////////////////////
// COleLinkingDoc OLE interface implementation

BEGIN_INTERFACE_MAP(COleLinkingDoc, COleDocument)
	INTERFACE_PART(COleLinkingDoc, IID_IPersist, PersistFile)
	INTERFACE_PART(COleLinkingDoc, IID_IPersistFile, PersistFile)
	INTERFACE_PART(COleLinkingDoc, IID_IParseDisplayName, OleItemContainer)
	INTERFACE_PART(COleLinkingDoc, IID_IOleContainer, OleItemContainer)
	INTERFACE_PART(COleLinkingDoc, IID_IOleItemContainer, OleItemContainer)
END_INTERFACE_MAP()

/////////////////////////////////////////////////////////////////////////////
// COleLinkingDoc::XPersistFile implementation

STDMETHODIMP_(ULONG) COleLinkingDoc::XPersistFile::AddRef()
{
	METHOD_PROLOGUE_EX_(COleLinkingDoc, PersistFile)
	return pThis->ExternalAddRef();
}

STDMETHODIMP_(ULONG) COleLinkingDoc::XPersistFile::Release()
{
	METHOD_PROLOGUE_EX_(COleLinkingDoc, PersistFile)
	return pThis->ExternalRelease();
}

STDMETHODIMP COleLinkingDoc::XPersistFile::QueryInterface(
	REFIID iid, LPVOID* ppvObj)
{
	METHOD_PROLOGUE_EX_(COleLinkingDoc, PersistFile)
	return pThis->ExternalQueryInterface(&iid, ppvObj);
}

STDMETHODIMP COleLinkingDoc::XPersistFile::GetClassID(LPCLSID lpClassID)
{
	METHOD_PROLOGUE_EX_(COleLinkingDoc, PersistFile)

	// this is sometimes called for documents not attached to servers!
	if (pThis->m_pFactory == NULL)
	{
		*lpClassID = CLSID_NULL;
		return E_FAIL;
	}

	// get the class ID from the connected server object
	ASSERT_VALID(pThis->m_pFactory);
	*lpClassID = pThis->m_pFactory->GetClassID();
	return S_OK;
}

STDMETHODIMP COleLinkingDoc::XPersistFile::IsDirty()
{
	METHOD_PROLOGUE_EX(COleLinkingDoc, PersistFile)
	return pThis->IsModified() ? S_OK : S_FALSE;
}

STDMETHODIMP COleLinkingDoc::XPersistFile::Load(
	LPCOLESTR lpszFileName, DWORD /*dwMode*/)
{
	METHOD_PROLOGUE_EX(COleLinkingDoc, PersistFile)
	ASSERT_VALID(pThis);

	USES_CONVERSION;

	CString strFileName;
	SCODE sc = E_FAIL;
	pThis->BeginDeferErrors();
	LPCTSTR lpszFileNameT = OLE2CT(lpszFileName);
	TRY
	{
		BOOL bUserCtrl = AfxOleGetUserCtrl();

		// delegate to file-based Open implementation
		if (!pThis->OnOpenDocument(lpszFileNameT))
		{
			AfxOleSetUserCtrl(bUserCtrl);
			return sc;
		}
		pThis->SendInitialUpdate();

		// set the path name, but don't add to MRU list
		pThis->SetPathName(lpszFileNameT, FALSE);
		AfxOleSetUserCtrl(bUserCtrl);

		sc = S_OK;
	}
	END_TRY
	sc = pThis->EndDeferErrors(sc);

	ASSERT_VALID(pThis);
	return sc;
}

STDMETHODIMP COleLinkingDoc::XPersistFile::Save(
	LPCOLESTR lpszFileName, BOOL fRemember)
{
	METHOD_PROLOGUE_EX(COleLinkingDoc, PersistFile)
	ASSERT_VALID(pThis);

	USES_CONVERSION;

	CString strFileName;
	SCODE sc = E_FAIL;
	pThis->BeginDeferErrors();
	TRY
	{
		// delegate to file-based Save/Save As implementation
		ASSERT(pThis->m_bRemember);
		pThis->m_bRemember = fRemember;
		pThis->OnSaveDocument(OLE2CT(lpszFileName));
		sc = S_OK;
	}
	END_TRY
	sc = pThis->EndDeferErrors(sc);

	ASSERT_VALID(pThis);
	return sc;
}

STDMETHODIMP COleLinkingDoc::XPersistFile::SaveCompleted(LPCOLESTR lpszFileName)
{
	METHOD_PROLOGUE_EX(COleLinkingDoc, PersistFile)
	ASSERT_VALID(pThis);

	USES_CONVERSION;

	TRY
	{
		// set the path name, but don't add to MRU list
		pThis->SetPathName(OLE2CT(lpszFileName), FALSE);
	}
	END_TRY

	ASSERT_VALID(pThis);
	return S_OK;
}

STDMETHODIMP COleLinkingDoc::XPersistFile::GetCurFile(LPOLESTR* lplpszFileName)
{
	METHOD_PROLOGUE_EX_(COleLinkingDoc, PersistFile)

	*lplpszFileName = NULL;

	// use title if no document
	LPCTSTR lpszResult;
	if (pThis->m_strPathName.IsEmpty())
		lpszResult = pThis->m_strTitle;
	else
		lpszResult = pThis->m_strPathName;
	ASSERT(lpszResult != NULL);

	// allocate memory for the file name
	*lplpszFileName = AfxAllocTaskOleString(lpszResult);
	if (*lplpszFileName == NULL)
		return E_OUTOFMEMORY;

	ASSERT_VALID(pThis);
	return S_OK;
}

/////////////////////////////////////////////////////////////////////////////
// Implementation of IOleItemContainer
//  (supports linking to embeddings and linking to pseudo-objects)

STDMETHODIMP_(ULONG) COleLinkingDoc::XOleItemContainer::AddRef()
{
	METHOD_PROLOGUE_EX_(COleLinkingDoc, OleItemContainer)
	return pThis->ExternalAddRef();
}

STDMETHODIMP_(ULONG) COleLinkingDoc::XOleItemContainer::Release()
{
	METHOD_PROLOGUE_EX_(COleLinkingDoc, OleItemContainer)
	return pThis->ExternalRelease();
}

STDMETHODIMP COleLinkingDoc::XOleItemContainer::QueryInterface(
	REFIID iid, LPVOID* ppvObj)
{
	METHOD_PROLOGUE_EX_(COleLinkingDoc, OleItemContainer)
	return pThis->ExternalQueryInterface(&iid, ppvObj);
}

STDMETHODIMP COleLinkingDoc::XOleItemContainer::EnumObjects(
	DWORD /*grfFlags*/, LPENUMUNKNOWN* ppEnumUnknown)
{
	*ppEnumUnknown = NULL;
	return E_NOTIMPL;
}

STDMETHODIMP COleLinkingDoc::XOleItemContainer::ParseDisplayName(LPBC lpbc,
	LPOLESTR lpszDisplayName, ULONG* cchEaten, LPMONIKER* ppMoniker)
{
	METHOD_PROLOGUE_EX_(COleLinkingDoc, OleItemContainer)

	USES_CONVERSION;

	// reset all OUT parameters
	*ppMoniker = NULL;

	TCHAR szItemName[OLE_MAXNAMESIZE];
	LPTSTR lpszDest = szItemName;
	LPCTSTR lpszSrc = OLE2CT(lpszDisplayName);

	// skip leading delimiters
	int cEaten = 0;
	while (*lpszSrc != '\0' && (*lpszSrc == '\\' || *lpszSrc == '/' ||
		*lpszSrc == ':' || *lpszSrc == '!' || *lpszSrc == '['))
	{
		if (_istlead(*lpszSrc))
			++lpszSrc, ++cEaten;
		++lpszSrc;
		++cEaten;
	}

	// parse next token in szItemName
	while (*lpszSrc != '\0' && *lpszSrc != '\\' && *lpszSrc != '/' &&
		*lpszSrc != ':' && *lpszSrc != '!' && *lpszSrc != '[' &&
		cEaten < OLE_MAXNAMESIZE-1)
	{
		if (_istlead(*lpszSrc))
			*lpszDest++ = *lpszSrc++, ++cEaten;
		*lpszDest++ = *lpszSrc++;
		++cEaten;
	}
	*cchEaten = cEaten;
	*lpszDest = 0;

	// attempt to get the object
	LPUNKNOWN lpUnknown;
	SCODE sc = GetObject(T2OLE(szItemName), BINDSPEED_INDEFINITE, lpbc,
		IID_IUnknown, (LPLP)&lpUnknown);
	if (sc != S_OK)
		return sc;

	// item name found -- create item moniker for it
	lpUnknown->Release();
	return CreateItemMoniker(OLESTDDELIMOLE, T2COLE(szItemName), ppMoniker);
}

STDMETHODIMP COleLinkingDoc::XOleItemContainer::LockContainer(BOOL fLock)
{
	METHOD_PROLOGUE_EX_(COleLinkingDoc, OleItemContainer)

	pThis->LockExternal(fLock, TRUE);
	return S_OK;
}

STDMETHODIMP COleLinkingDoc::XOleItemContainer::GetObject(
	LPOLESTR lpszItem, DWORD dwSpeedNeeded, LPBINDCTX /*pbc*/, REFIID riid,
	LPVOID* ppvObject)
{
	METHOD_PROLOGUE_EX(COleLinkingDoc, OleItemContainer)
	ASSERT_VALID(pThis);

	USES_CONVERSION;

	*ppvObject = NULL;

	SCODE sc = MK_E_NOOBJECT;
	TRY
	{
		LPCTSTR lpszItemT = OLE2CT(lpszItem);
		// check for link to embedding
		COleClientItem* pClientItem = pThis->OnFindEmbeddedItem(lpszItemT);
		if (pClientItem != NULL)
		{
			ASSERT_VALID(pClientItem);
			sc = S_OK;

			// item found -- make sure it is running
			if (!::OleIsRunning(pClientItem->m_lpObject))
			{
				// should not run the object if bind-speed is immediate
				if (dwSpeedNeeded != BINDSPEED_INDEFINITE)
					sc = MK_E_EXCEEDEDDEADLINE;
				else
				{
					// bind speed is not immediate -- so run the object
					sc = OleRun(pClientItem->m_lpObject);
				}
			}

			if (sc == S_OK)
			{
				// return the object with appropriate interface
				sc = pClientItem->m_lpObject->QueryInterface(riid, ppvObject);
			}
		}
		else
		{
			// check for link to pseudo object
			COleServerItem* pServerItem = pThis->OnGetLinkedItem(lpszItemT);
			if (pServerItem != NULL)
			{
				if (!pServerItem->m_bNeedUnlock)
				{
					// when a link is bound, the document must be kept alive
					pThis->LockExternal(TRUE, FALSE);
					pServerItem->m_bNeedUnlock = TRUE;
				}

				// matching item found -- query for the requested interface
				sc = pServerItem->ExternalQueryInterface(&riid, ppvObject);
			}
		}
	}
	END_TRY

	return sc;
}

STDMETHODIMP COleLinkingDoc::XOleItemContainer::GetObjectStorage(
	LPOLESTR lpszItem, LPBINDCTX /*pbc*/, REFIID riid, LPVOID* ppvStorage)
{
	METHOD_PROLOGUE_EX(COleLinkingDoc, OleItemContainer)
	ASSERT_VALID(pThis);

	USES_CONVERSION;

	*ppvStorage = NULL;

	// only IStorage is supported
	if (riid != IID_IStorage)
		return E_UNEXPECTED;

	// check for link to embedding
	COleClientItem* pClientItem = pThis->OnFindEmbeddedItem(OLE2CT(lpszItem));
	if (pClientItem != NULL)
	{
		ASSERT_VALID(pClientItem);

		// if object has no storage, can't return it!
		if (pClientItem->m_lpStorage != NULL)
		{
			// found matching item -- return the storage
			*ppvStorage = pClientItem->m_lpStorage;
			pClientItem->m_lpStorage->AddRef();
			return S_OK;
		}
	}
	return MK_E_NOSTORAGE;
}

STDMETHODIMP COleLinkingDoc::XOleItemContainer::IsRunning(LPOLESTR lpszItem)
{
	METHOD_PROLOGUE_EX(COleLinkingDoc, OleItemContainer)
	ASSERT_VALID(pThis);

	USES_CONVERSION;

	// check for link to embedding
	LPCTSTR lpszItemT = OLE2CT(lpszItem);
	COleClientItem* pClientItem = pThis->OnFindEmbeddedItem(lpszItemT);
	if (pClientItem != NULL)
	{
		ASSERT_VALID(pClientItem);
		if (!::OleIsRunning(pClientItem->m_lpObject))
			return S_FALSE;

		return S_OK; // item is in document and is running
	}

	// check for link to pseudo object
	SCODE sc = MK_E_NOOBJECT;
	TRY
	{
		COleServerItem* pServerItem = pThis->OnGetLinkedItem(lpszItemT);
		if (pServerItem != NULL)
			sc = S_OK;
	}
	END_TRY

	return sc;
}

/////////////////////////////////////////////////////////////////////////////
// COleLinkingDoc diagnostics

#ifdef _DEBUG
void COleLinkingDoc::AssertValid() const
{
	COleDocument::AssertValid();
	if (m_pFactory != NULL)
		m_pFactory->AssertValid();
}

void COleLinkingDoc::Dump(CDumpContext& dc) const
{
	COleDocument::Dump(dc);

	dc << "\nm_dwRegister = " << m_dwRegister;
	dc << "\nm_bVisibleLock = " << m_bVisibleLock;
	if (m_pFactory != NULL)
		dc << "\nwith factory: " << m_pFactory;
	else
		dc << "\nwith no factory";

	dc << "\n";
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
