LINKING.C

/************************************************************************* 
**
** OLE 2 Sample Code
**
** linking.c
**
** This file contains the major interfaces, methods and related support
** functions for implementing linking to items. The code
** contained in this file is used by BOTH the Container and Server
** (Object) versions of the Outline sample code.
**
** As a server SVROUTL supports linking to the whole document object
** (either a file-based document or as an embedded object). It also
** supports linking to ranges (or PseudoObjects).
**
** As a container CNTROUTL supports linking to embedded objects.
** (see file svrpsobj.c for Pseudo Object implementation)
**
** OleDoc Object
** exposed interfaces:
** IPersistFile
** IOleItemContainer
** IExternalConnection
**
** (c) Copyright Microsoft Corp. 1992 - 1997 All Rights Reserved
**
*************************************************************************/

#include "outline.h"

OLEDBGDATA

extern LPOUTLINEAPP g_lpApp;


/*************************************************************************
** OleDoc::IPersistFile interface implementation
*************************************************************************/

// IPersistFile::QueryInterface
STDMETHODIMP OleDoc_PFile_QueryInterface(
LPPERSISTFILE lpThis,
REFIID riid,
LPVOID FAR* lplpvObj
)
{
LPOLEDOC lpOleDoc = ((struct CDocPersistFileImpl FAR*)lpThis)->lpOleDoc;

return OleDoc_QueryInterface(lpOleDoc, riid, lplpvObj);
}


// IPersistFile::AddRef
STDMETHODIMP_(ULONG) OleDoc_PFile_AddRef(LPPERSISTFILE lpThis)
{
LPOLEDOC lpOleDoc = ((struct CDocPersistFileImpl FAR*)lpThis)->lpOleDoc;

OleDbgAddRefMethod(lpThis, "IPersistFile");

return OleDoc_AddRef(lpOleDoc);
}


// IPersistFile::Release
STDMETHODIMP_(ULONG) OleDoc_PFile_Release (LPPERSISTFILE lpThis)
{
LPOLEDOC lpOleDoc = ((struct CDocPersistFileImpl FAR*)lpThis)->lpOleDoc;

OleDbgReleaseMethod(lpThis, "IPersistFile");

return OleDoc_Release(lpOleDoc);
}


// IPersistFile::GetClassID
STDMETHODIMP OleDoc_PFile_GetClassID (
LPPERSISTFILE lpThis,
CLSID FAR* lpclsid
)
{
LPOLEDOC lpOleDoc = ((struct CDocPersistFileImpl FAR*)lpThis)->lpOleDoc;
LPOLEAPP lpOleApp = (LPOLEAPP)g_lpApp;
OleDbgOut2("OleDoc_PFile_GetClassID\r\n");

#if defined( OLE_SERVER ) && defined( SVR_TREATAS )

/* NOTE: we must be carefull to return the correct CLSID here.
** if we are currently preforming a "TreatAs (aka. ActivateAs)"
** operation then we need to return the class of the object
** written in the storage of the object. otherwise we would
** return our own class id.
*/
return ServerDoc_GetClassID((LPSERVERDOC)lpOleDoc, lpclsid);
#else
*lpclsid = CLSID_APP;
#endif
return NOERROR;
}


// IPersistFile::IsDirty
STDMETHODIMP OleDoc_PFile_IsDirty(LPPERSISTFILE lpThis)
{
LPOLEDOC lpOleDoc = ((struct CDocPersistFileImpl FAR*)lpThis)->lpOleDoc;
OleDbgOut2("OleDoc_PFile_IsDirty\r\n");

if (OutlineDoc_IsModified((LPOUTLINEDOC)lpOleDoc))
return NOERROR;
else
return S_FALSE;
}


// IPersistFile::Load
STDMETHODIMP OleDoc_PFile_Load (
LPPERSISTFILE lpThis,
LPCOLESTR lpszFileName,
DWORD grfMode
)
{
LPOLEDOC lpOleDoc = ((struct CDocPersistFileImpl FAR*)lpThis)->lpOleDoc;
SCODE sc;

OLEDBG_BEGIN2("OleDoc_PFile_Load\r\n")

/* NOTE: grfMode passed from the caller indicates if the caller
** needs Read or ReadWrite permissions. if appropriate the
** callee should open the file with the requested permissions.
** the caller will normally not impose sharing permissions.
**
** the sample code currently always opens its file ReadWrite.
*/

if (OutlineDoc_LoadFromFile((LPOUTLINEDOC)lpOleDoc, (LPOLESTR)lpszFileName))
sc = S_OK;
else
sc = E_FAIL;

OLEDBG_END2
return sc;
}


// IPersistFile::Save
STDMETHODIMP OleDoc_PFile_Save (
LPPERSISTFILE lpThis,
LPCOLESTR lpszFileName,
BOOL fRemember
)
{
LPOLEDOC lpOleDoc = ((struct CDocPersistFileImpl FAR*)lpThis)->lpOleDoc;
LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpOleDoc;
SCODE sc;

OLEDBG_BEGIN2("OleDoc_PFile_Save\r\n")

/* NOTE: it is only legal to perform a Save or SaveAs operation
** on a file-based document. if the document is an embedded
** object then we can not be changed to a file-base object.
**
** fRemember lpszFileName Type of Save
** ----------------------------------------------
** TRUE NULL SAVE
** TRUE ! NULL SAVE AS
** FALSE ! NULL SAVE COPY AS
** FALSE NULL ***error***
*/
if ( (lpszFileName==NULL || (lpszFileName != NULL && fRemember))
&& ((lpOutlineDoc->m_docInitType != DOCTYPE_FROMFILE
&& lpOutlineDoc->m_docInitType != DOCTYPE_NEW)) ) {
OLEDBG_END2
return E_INVALIDARG;
}

if (OutlineDoc_SaveToFile(
(LPOUTLINEDOC)lpOleDoc,
lpszFileName,
lpOutlineDoc->m_cfSaveFormat,
fRemember)) {
sc = S_OK;
} else
sc = E_FAIL;

OLEDBG_END2
return sc;
}


// IPersistFile::SaveCompleted
STDMETHODIMP OleDoc_PFile_SaveCompleted (
LPPERSISTFILE lpThis,
LPCOLESTR lpszFileName
)
{
LPOLEDOC lpOleDoc = ((struct CDocPersistFileImpl FAR*)lpThis)->lpOleDoc;
LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpOleDoc;

OleDbgOut2("OleDoc_PFile_SaveCompleted\r\n");

/* This method is called after IPersistFile::Save is called. during
** the period between Save and SaveCompleted the object must
** consider itself in NOSCRIBBLE mode (ie. it is NOT allowed to
** write to its file. here the object can clear its NOSCRIBBLE
** mode flag. the outline app never scribbles to its storage, so
** we have nothing to do.
*/
return NOERROR;
}


// IPersistFile::GetCurFile
STDMETHODIMP OleDoc_PFile_GetCurFile (
LPPERSISTFILE lpThis,
LPOLESTR FAR* lplpszFileName
)
{
LPOLEDOC lpOleDoc = ((struct CDocPersistFileImpl FAR*)lpThis)->lpOleDoc;
LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpOleDoc;
LPMALLOC lpMalloc;
LPOLESTR lpsz;
SCODE sc;
char szAnsiStr[256];

OleDbgOut2("OleDoc_PFile_GetCurFile\r\n");

/* NOTE: we must make sure to set all out ptr parameters to NULL. */
*lplpszFileName = NULL;

/*********************************************************************
** NOTE: memory returned for the lplpszFileName must be
** allocated appropriately using the current registered IMalloc
** interface. the allows the ownership of the memory to be
** passed to the caller (even if in another process).
*********************************************************************/

CoGetMalloc(MEMCTX_TASK, &lpMalloc);
if (! lpMalloc) {

return E_FAIL;
}

if (lpOutlineDoc->m_docInitType == DOCTYPE_FROMFILE) {
/* valid filename associated; return file name */
lpsz = /*(LPOLESTR)*/lpMalloc->lpVtbl->Alloc(
lpMalloc,
(OLESTRLEN(lpOutlineDoc->m_szFileName)+1)*sizeof(OLECHAR)
);
if (! lpsz) {
sc = E_OUTOFMEMORY;
goto error;
}

OLESTRCPY(lpsz, /*(LPOLESTR)*/lpOutlineDoc->m_szFileName);
sc = S_OK;
} else {
/* no file associated; return default file name prompt */
lpsz=/*(LPOLESTR)*/lpMalloc->lpVtbl->Alloc(lpMalloc, (lstrlen(DEFEXTENSION)+3)*sizeof(OLECHAR));
wsprintf(szAnsiStr, "*.%s", DEFEXTENSION);
A2W (szAnsiStr, lpsz, OLEUI_CCHPATHMAX);
sc = S_FALSE;
}

error:
OleStdRelease((LPUNKNOWN)lpMalloc);
*lplpszFileName = lpsz;


return sc;
}


/*************************************************************************
** OleDoc::IOleItemContainer interface implementation
*************************************************************************/

// IOleItemContainer::QueryInterface
STDMETHODIMP OleDoc_ItemCont_QueryInterface(
LPOLEITEMCONTAINER lpThis,
REFIID riid,
LPVOID FAR* lplpvObj
)
{
LPOLEDOC lpOleDoc =
((struct CDocOleItemContainerImpl FAR*)lpThis)->lpOleDoc;

return OleDoc_QueryInterface(lpOleDoc, riid, lplpvObj);
}


// IOleItemContainer::AddRef
STDMETHODIMP_(ULONG) OleDoc_ItemCont_AddRef(LPOLEITEMCONTAINER lpThis)
{
LPOLEDOC lpOleDoc =
((struct CDocOleItemContainerImpl FAR*)lpThis)->lpOleDoc;

OleDbgAddRefMethod(lpThis, "IOleItemContainer");

return OleDoc_AddRef((LPOLEDOC)lpOleDoc);
}


// IOleItemContainer::Release
STDMETHODIMP_(ULONG) OleDoc_ItemCont_Release(LPOLEITEMCONTAINER lpThis)
{
LPOLEDOC lpOleDoc =
((struct CDocOleItemContainerImpl FAR*)lpThis)->lpOleDoc;

OleDbgReleaseMethod(lpThis, "IOleItemContainer");

return OleDoc_Release((LPOLEDOC)lpOleDoc);
}


// IOleItemContainer::ParseDisplayName
STDMETHODIMP OleDoc_ItemCont_ParseDisplayName(
LPOLEITEMCONTAINER lpThis,
LPBC lpbc,
LPOLESTR lpszDisplayName,
ULONG FAR* lpchEaten,
LPMONIKER FAR* lplpmkOut
)
{
LPOLEDOC lpOleDoc =
((struct CDocOleItemContainerImpl FAR*)lpThis)->lpOleDoc;
OLECHAR szItemName[MAXNAMESIZE];
LPUNKNOWN lpUnk;
HRESULT hrErr;

OleDbgOut2("OleDoc_ItemCont_ParseDisplayName\r\n");

/* NOTE: we must make sure to set all out ptr parameters to NULL. */
*lplpmkOut = NULL;

*lpchEaten = OleStdGetItemToken(
lpszDisplayName,
szItemName,
MAXNAMESIZE*sizeof(OLECHAR)
);

/* NOTE: get a pointer to a running instance of the object. we
** should force the object to go running if necessary (even if
** this means launching its server EXE). this is the meaining of
** BINDSPEED_INDEFINITE. Parsing a Moniker is known to be an
** "EXPENSIVE" operation.
*/
hrErr = OleDoc_ItemCont_GetObject(
lpThis,
szItemName,
BINDSPEED_INDEFINITE,
lpbc,
&IID_IUnknown,
(LPVOID FAR*)&lpUnk
);

if (hrErr == NOERROR) {
OleStdRelease(lpUnk); // item name FOUND; don't need obj ptr.
CreateItemMoniker(OLESTR("\\"), szItemName, lplpmkOut);
} else
*lpchEaten = 0; // item name is NOT valid

return hrErr;
}


// IOleItemContainer::EnumObjects
STDMETHODIMP OleDoc_ItemCont_EnumObjects(
LPOLEITEMCONTAINER lpThis,
DWORD grfFlags,
LPENUMUNKNOWN FAR* lplpenumUnknown
)
{
LPOLEDOC lpOleDoc =
((struct CDocOleItemContainerImpl FAR*)lpThis)->lpOleDoc;

OLEDBG_BEGIN2("OleDoc_ItemCont_EnumObjects\r\n")

/* NOTE: we must make sure to set all out ptr parameters to NULL. */
*lplpenumUnknown = NULL;

/* NOTE: this method should be implemented to allow programatic
** clients the ability to what elements the container holds.
** this method is NOT called in the standard linking scenarios.
**
** grfFlags can be one of the following:
** OLECONTF_EMBEDDINGS -- enumerate embedded objects
** OLECONTF_LINKS -- enumerate linked objects
** OLECONTF_OTHERS -- enumerate non-OLE compound doc objs
** OLECONTF_ONLYUSER -- enumerate only objs named by user
** OLECONTF_ONLYIFRUNNING-- enumerate only objs in running state
*/

OleDbgAssertSz(0, "NOT YET IMPLEMENTED!");

OLEDBG_END2

return E_NOTIMPL;
}


// IOleItemContainer::LockContainer
STDMETHODIMP OleDoc_ItemCont_LockContainer(
LPOLEITEMCONTAINER lpThis,
BOOL fLock
)
{
LPOLEDOC lpOleDoc =
((struct CDocOleItemContainerImpl FAR*)lpThis)->lpOleDoc;
HRESULT hrErr;
OLEDBG_BEGIN2("OleDoc_ItemCont_LockContainer\r\n")

#if defined( _DEBUG )
if (fLock) {
++lpOleDoc->m_cCntrLock;
OleDbgOutRefCnt3(
"OleDoc_ItemCont_LockContainer: cLock++\r\n",
lpOleDoc,
lpOleDoc->m_cCntrLock
);
} else {
/* NOTE: when there are no open documents and the app is not
** under the control of the user and there are no outstanding
** locks on the app, then revoke our ClassFactory to enable the
** app to shut down.
*/
--lpOleDoc->m_cCntrLock;
OleDbgAssertSz (
lpOleDoc->m_cCntrLock >= 0,
"OleDoc_ItemCont_LockContainer(FALSE) called with cLock == 0"
);

if (lpOleDoc->m_cCntrLock == 0) {
OleDbgOutRefCnt2(
"OleDoc_ItemCont_LockContainer: UNLOCKED\r\n",
lpOleDoc, lpOleDoc->m_cCntrLock);
} else {
OleDbgOutRefCnt3(
"OleDoc_ItemCont_LockContainer: cLock--\r\n",
lpOleDoc, lpOleDoc->m_cCntrLock);
}
}
#endif // _DEBUG

/* NOTE: in order to hold the document alive we call
** CoLockObjectExternal to add a strong reference to our Doc
** object. this will keep the Doc alive when all other external
** references release us. whenever an embedded object goes
** running a LockContainer(TRUE) is called. when the embedded
** object shuts down (ie. transitions from running to loaded)
** LockContainer(FALSE) is called. if the user issues File.Close
** the document will shut down in any case ignoring any
** outstanding LockContainer locks because CoDisconnectObject is
** called in OleDoc_Close. this will forceably break any
** existing strong reference counts including counts that we add
** ourselves by calling CoLockObjectExternal and guarantee that
** the Doc object gets its final release (ie. cRefs goes to 0).
*/
hrErr = OleDoc_Lock(lpOleDoc, fLock, TRUE /* fLastUnlockReleases */);

OLEDBG_END2
return hrErr;
}


// IOleItemContainer::GetObject
STDMETHODIMP OleDoc_ItemCont_GetObject(
LPOLEITEMCONTAINER lpThis,
LPOLESTR lpszItem,
DWORD dwSpeedNeeded,
LPBINDCTX lpbc,
REFIID riid,
LPVOID FAR* lplpvObject
)
{
LPOLEDOC lpOleDoc =
((struct CDocOleItemContainerImpl FAR*)lpThis)->lpOleDoc;
HRESULT hrErr;

OLEDBG_BEGIN2("OleDoc_ItemCont_GetObject\r\n")

/* NOTE: we must make sure to set all out ptr parameters to NULL. */
*lplpvObject = NULL;

#if defined( OLE_SERVER )

/* NOTE: SERVER ONLY version should return PseudoObjects with
** BINDSPEED_IMMEDIATE, thus the dwSpeedNeeded is not important
** in the case of a pure server.
*/
hrErr = ServerDoc_GetObject(
(LPSERVERDOC)lpOleDoc, lpszItem,riid,lplpvObject);
#endif
#if defined( OLE_CNTR )

/* NOTE: dwSpeedNeeded indicates how long the caller is willing
** to wait for us to get the object:
** BINDSPEED_IMMEDIATE -- only if obj already loaded && IsRunning
** BINDSPEED_MODERATE -- load obj if necessary && if IsRunning
** BINDSPEED_INDEFINITE-- force obj to load and run if necessary
*/
hrErr = ContainerDoc_GetObject(
(LPCONTAINERDOC)lpOleDoc,lpszItem,dwSpeedNeeded,riid,lplpvObject);
#endif

OLEDBG_END2

return hrErr;
}


// IOleItemContainer::GetObjectStorage
STDMETHODIMP OleDoc_ItemCont_GetObjectStorage(
LPOLEITEMCONTAINER lpThis,
LPOLESTR lpszItem,
LPBINDCTX lpbc,
REFIID riid,
LPVOID FAR* lplpvStorage
)
{
LPOLEDOC lpOleDoc =
((struct CDocOleItemContainerImpl FAR*)lpThis)->lpOleDoc;
OleDbgOut2("OleDoc_ItemCont_GetObjectStorage\r\n");

/* NOTE: we must make sure to set all out ptr parameters to NULL. */
*lplpvStorage = NULL;

#if defined( OLE_SERVER )
/* NOTE: in the SERVER ONLY version, item names identify pseudo
** objects. pseudo objects, do NOT have identifiable storage.
*/
return E_FAIL;
#endif
#if defined( OLE_CNTR )
// We can only return an IStorage* type pointer
if (! IsEqualIID(riid, &IID_IStorage))
{
return E_FAIL;
}

return ContainerDoc_GetObjectStorage(
(LPCONTAINERDOC)lpOleDoc,
lpszItem,
(LPSTORAGE FAR*)lplpvStorage
);
#endif
}


// IOleItemContainer::IsRunning
STDMETHODIMP OleDoc_ItemCont_IsRunning(
LPOLEITEMCONTAINER lpThis,
LPOLESTR lpszItem
)
{
LPOLEDOC lpOleDoc =
((struct CDocOleItemContainerImpl FAR*)lpThis)->lpOleDoc;
HRESULT hrErr;

OLEDBG_BEGIN2("OleDoc_ItemCont_IsRunning\r\n")

/* NOTE: Check if item name is valid. if so then return if
** Object is running. PseudoObjects in the Server version are
** always considered running. Ole objects in the container must
** be checked if they are running.
*/

#if defined( OLE_SERVER )
hrErr = ServerDoc_IsRunning((LPSERVERDOC)lpOleDoc, lpszItem);
#endif
#if defined( OLE_CNTR )
hrErr = ContainerDoc_IsRunning((LPCONTAINERDOC)lpOleDoc, lpszItem);
#endif

OLEDBG_END2
return hrErr;
}


/*************************************************************************
** OleDoc::IExternalConnection interface implementation
*************************************************************************/

// IExternalConnection::QueryInterface
STDMETHODIMP OleDoc_ExtConn_QueryInterface(
LPEXTERNALCONNECTION lpThis,
REFIID riid,
LPVOID FAR* lplpvObj
)
{
LPOLEDOC lpOleDoc =
((struct CDocExternalConnectionImpl FAR*)lpThis)->lpOleDoc;

return OleDoc_QueryInterface(lpOleDoc, riid, lplpvObj);
}


// IExternalConnection::AddRef
STDMETHODIMP_(ULONG) OleDoc_ExtConn_AddRef(LPEXTERNALCONNECTION lpThis)
{
LPOLEDOC lpOleDoc =
((struct CDocExternalConnectionImpl FAR*)lpThis)->lpOleDoc;

OleDbgAddRefMethod(lpThis, "IExternalConnection");

return OleDoc_AddRef(lpOleDoc);
}


// IExternalConnection::Release
STDMETHODIMP_(ULONG) OleDoc_ExtConn_Release (LPEXTERNALCONNECTION lpThis)
{
LPOLEDOC lpOleDoc =
((struct CDocExternalConnectionImpl FAR*)lpThis)->lpOleDoc;

OleDbgReleaseMethod(lpThis, "IExternalConnection");

return OleDoc_Release(lpOleDoc);
}


// IExternalConnection::AddConnection
STDMETHODIMP_(DWORD) OleDoc_ExtConn_AddConnection(
LPEXTERNALCONNECTION lpThis,
DWORD extconn,
DWORD reserved
)
{
LPOLEDOC lpOleDoc =
((struct CDocExternalConnectionImpl FAR*)lpThis)->lpOleDoc;

if( extconn & EXTCONN_STRONG ) {

#if defined( _DEBUG )
OleDbgOutRefCnt3(
"OleDoc_ExtConn_AddConnection: dwStrongExtConn++\r\n",
lpOleDoc,
lpOleDoc->m_dwStrongExtConn + 1
);
#endif
return ++(lpOleDoc->m_dwStrongExtConn);
} else
{
return 0;
}
}


// IExternalConnection::ReleaseConnection
STDMETHODIMP_(DWORD) OleDoc_ExtConn_ReleaseConnection(
LPEXTERNALCONNECTION lpThis,
DWORD extconn,
DWORD reserved,
BOOL fLastReleaseCloses
)
{
LPOLEDOC lpOleDoc =
((struct CDocExternalConnectionImpl FAR*)lpThis)->lpOleDoc;

if( extconn & EXTCONN_STRONG ){
DWORD dwSave = --(lpOleDoc->m_dwStrongExtConn);
#if defined( _DEBUG )
OLEDBG_BEGIN2( (fLastReleaseCloses ?
"OleDoc_ExtConn_ReleaseConnection(TRUE)\r\n" :
"OleDoc_ExtConn_ReleaseConnection(FALSE)\r\n") )
OleDbgOutRefCnt3(
"OleDoc_ExtConn_ReleaseConnection: dwStrongExtConn--\r\n",
lpOleDoc,
lpOleDoc->m_dwStrongExtConn
);
OleDbgAssertSz (
lpOleDoc->m_dwStrongExtConn >= 0,
"OleDoc_ExtConn_ReleaseConnection called with dwStrong == 0"
);
#endif // _DEBUG

if( lpOleDoc->m_dwStrongExtConn == 0 && fLastReleaseCloses )
OleDoc_Close(lpOleDoc, OLECLOSE_SAVEIFDIRTY);

OLEDBG_END2
return dwSave;
} else
{
return 0;
}
}


/*************************************************************************
** OleDoc Common Support Functions
*************************************************************************/


/* OleDoc_GetFullMoniker
** ---------------------
** Return the full, absolute moniker of the document.
**
** NOTE: the caller must release the pointer returned when done.
*/
LPMONIKER OleDoc_GetFullMoniker(LPOLEDOC lpOleDoc, DWORD dwAssign)
{
LPMONIKER lpMoniker = NULL;

OLEDBG_BEGIN3("OleDoc_GetFullMoniker\r\n")

if (lpOleDoc->m_lpSrcDocOfCopy) {
/* CASE I: this document was created for a copy or drag/drop
** operation. generate the moniker which identifies the
** source document of the original copy.
*/
if (! lpOleDoc->m_fLinkSourceAvail)
goto done; // we already know a moniker is not available

lpMoniker=OleDoc_GetFullMoniker(lpOleDoc->m_lpSrcDocOfCopy, dwAssign);
}
else if (lpOleDoc->m_lpFileMoniker) {

/* CASE II: this document is a top-level user document (either
** file-based or untitled). return the FileMoniker stored
** with the document; it uniquely identifies the document.
*/
// we must AddRef the moniker to pass out a ptr
lpOleDoc->m_lpFileMoniker->lpVtbl->AddRef(lpOleDoc->m_lpFileMoniker);

lpMoniker = lpOleDoc->m_lpFileMoniker;
}

#if defined( OLE_SERVER )

else if (((LPSERVERDOC)lpOleDoc)->m_lpOleClientSite) {

/* CASE III: this document is an embedded object, ask our
** container for our moniker.
*/
OLEDBG_BEGIN2("IOleClientSite::GetMoniker called\r\n");
((LPSERVERDOC)lpOleDoc)->m_lpOleClientSite->lpVtbl->GetMoniker(
((LPSERVERDOC)lpOleDoc)->m_lpOleClientSite,
dwAssign,
OLEWHICHMK_OBJFULL,
&lpMoniker
);
OLEDBG_END2
}

#endif

else {
lpMoniker = NULL;
}

done:
OLEDBG_END3
return lpMoniker;
}


/* OleDoc_DocRenamedUpdate
** -----------------------
** Update the documents registration in the running object table (ROT).
** Also inform all embedded OLE objects (container only) and/or psedudo
** objects (server only) that the name of the document has changed.
*/
void OleDoc_DocRenamedUpdate(LPOLEDOC lpOleDoc, LPMONIKER lpmkDoc)
{
OLEDBG_BEGIN3("OleDoc_DocRenamedUpdate\r\n")

OleDoc_AddRef(lpOleDoc);

/* NOTE: we must re-register ourselves as running when we
** get a new moniker assigned (ie. when we are renamed).
*/
OLEDBG_BEGIN3("OleStdRegisterAsRunning called\r\n")
OleStdRegisterAsRunning(
(LPUNKNOWN)&lpOleDoc->m_Unknown,
lpmkDoc,
&lpOleDoc->m_dwRegROT
);
OLEDBG_END3

#if defined( OLE_SERVER )
{
LPSERVERDOC lpServerDoc = (LPSERVERDOC)lpOleDoc;
LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpOleDoc;

/* NOTE: inform any linking clients that the document has been
** renamed.
*/
ServerDoc_SendAdvise (
lpServerDoc,
OLE_ONRENAME,
lpmkDoc,
0 /* advf -- not relevant here */
);

/* NOTE: inform any clients of pseudo objects
** within our document, that our document's
** Moniker has changed.
*/
ServerNameTable_InformAllPseudoObjectsDocRenamed(
(LPSERVERNAMETABLE)lpOutlineDoc->m_lpNameTable, lpmkDoc);
}
#endif
#if defined( OLE_CNTR )
{
LPCONTAINERDOC lpContainerDoc = (LPCONTAINERDOC)lpOleDoc;

/* NOTE: must tell all OLE objects that our container
** moniker changed.
*/
ContainerDoc_InformAllOleObjectsDocRenamed(
lpContainerDoc,
lpmkDoc
);
}
#endif

OleDoc_Release(lpOleDoc); // release artificial AddRef above
OLEDBG_END3
}



#if defined( OLE_SERVER )

/*************************************************************************
** ServerDoc Supprt Functions Used by Server versions
*************************************************************************/


/* ServerDoc_PseudoObjLockDoc
** --------------------------
** Add a lock on the Doc on behalf of the PseudoObject. the Doc may not
** close while the Doc exists.
**
** when a pseudo object is first created, it calls this method to
** guarantee that the document stays alive (PseudoObj_Init).
** when a pseudo object is destroyed, it call
** ServerDoc_PseudoObjUnlockDoc to release this hold on the document.
*/
void ServerDoc_PseudoObjLockDoc(LPSERVERDOC lpServerDoc)
{
LPOLEDOC lpOleDoc = (LPOLEDOC)lpServerDoc;
ULONG cPseudoObj;

cPseudoObj = ++lpServerDoc->m_cPseudoObj;

#if defined( _DEBUG )
OleDbgOutRefCnt3(
"ServerDoc_PseudoObjLockDoc: cPseudoObj++\r\n",
lpServerDoc,
cPseudoObj
);
#endif
OleDoc_Lock(lpOleDoc, TRUE /* fLock */, 0 /* not applicable */);
return;
}


/* ServerDoc_PseudoObjUnlockDoc
** ----------------------------
** Release the lock on the Doc on behalf of the PseudoObject. if this was
** the last lock on the Doc, then it will shutdown.
*/
void ServerDoc_PseudoObjUnlockDoc(
LPSERVERDOC lpServerDoc,
LPPSEUDOOBJ lpPseudoObj
)
{
ULONG cPseudoObj;
LPOLEDOC lpOleDoc = (LPOLEDOC)lpServerDoc;
OLEDBG_BEGIN3("ServerDoc_PseudoObjUnlockDoc\r\n")

/* NOTE: when there are no active pseudo objects in the Doc and
** the Doc is not visible, and if there are no outstanding locks
** on the Doc, then this is a "silent update"
** situation. our Doc is being used programatically by some
** client; it is NOT accessible to the user because it is
** NOT visible. thus since all Locks have been released, we
** will close the document. if the app is only running due
** to the presence of this document, then the app will now
** also shut down.
*/
cPseudoObj = --lpServerDoc->m_cPseudoObj;

#if defined( _DEBUG )
OleDbgAssertSz (
lpServerDoc->m_cPseudoObj >= 0,
"PseudoObjUnlockDoc called with cPseudoObj == 0"
);

OleDbgOutRefCnt3(
"ServerDoc_PseudoObjUnlockDoc: cPseudoObj--\r\n",
lpServerDoc,
cPseudoObj
);
#endif
OleDoc_Lock(lpOleDoc, FALSE /* fLock */, TRUE /* fLastUnlockReleases */);

OLEDBG_END3
return;
}


/* ServerDoc_GetObject
** -------------------
**
** Return a pointer to an object identified by an item string
** (lpszItem). For a server-only app, the object returned will be a
** pseudo object.
*/
HRESULT ServerDoc_GetObject(
LPSERVERDOC lpServerDoc,
LPOLESTR lpszItem,
REFIID riid,
LPVOID FAR* lplpvObject
)
{
LPPSEUDOOBJ lpPseudoObj;
LPSERVERNAMETABLE lpServerNameTable =
(LPSERVERNAMETABLE)((LPOUTLINEDOC)lpServerDoc)->m_lpNameTable;

*lplpvObject = NULL;

/* Get the PseudoObj which corresponds to an item name. if the item
** name does NOT exist in the name table then NO object is
** returned. the ServerNameTable_GetPseudoObj routine finds a
** name entry corresponding to the item name, it then checks if
** a PseudoObj has already been allocated. if so, it returns the
** existing object, otherwise it allocates a new PseudoObj.
*/
lpPseudoObj = ServerNameTable_GetPseudoObj(
lpServerNameTable,
lpszItem,
lpServerDoc
);

if (! lpPseudoObj) {
*lplpvObject = NULL;
return MK_E_NOOBJECT;
}

// return the desired interface pointer of the pseudo object.
return PseudoObj_QueryInterface(lpPseudoObj, riid, lplpvObject);
}


/* ServerDoc_IsRunning
** -------------------
**
** Check if the object identified by an item string (lpszItem) is in
** the running state. For a server-only app, if the item name exists in
** in the NameTable then the item name is considered running.
** IOleItemContainer::GetObject would succeed.
*/

HRESULT ServerDoc_IsRunning(LPSERVERDOC lpServerDoc, LPOLESTR lpszItem)

{ 
LPOUTLINENAMETABLE lpOutlineNameTable =
((LPOUTLINEDOC)lpServerDoc)->m_lpNameTable;
LPSERVERNAME lpServerName;

lpServerName = (LPSERVERNAME)OutlineNameTable_FindName(
lpOutlineNameTable,
lpszItem
);

if (lpServerName)
return NOERROR;
else
return MK_E_NOOBJECT;
}


/* ServerDoc_GetSelRelMoniker
** --------------------------
** Retrieve the relative item moniker which identifies the given
** selection (lplrSel).
**
** Returns NULL if a moniker can NOT be created.
*/

LPMONIKER ServerDoc_GetSelRelMoniker(
LPSERVERDOC lpServerDoc,
LPLINERANGE lplrSel,
DWORD dwAssign
)
{
LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpServerDoc;
LPSERVERNAMETABLE lpServerNameTable =
(LPSERVERNAMETABLE)lpOutlineDoc->m_lpNameTable;
LPOUTLINENAMETABLE lpOutlineNameTable =
(LPOUTLINENAMETABLE)lpServerNameTable;
LPOUTLINENAME lpOutlineName;
LPMONIKER lpmk;

lpOutlineName=OutlineNameTable_FindNamedRange(lpOutlineNameTable,lplrSel);

if (lpOutlineName) {
/* the selection range already has a name assigned */
CreateItemMoniker(OLESTR("\\"), lpOutlineName->m_szName, &lpmk);
} else {
char szbuf[MAXNAMESIZE];
OLECHAR szUniBuf[MAXNAMESIZE];

switch (dwAssign) {

case GETMONIKER_FORCEASSIGN:

/* Force the assignment of the name. This is called when a
** Paste Link actually occurs. At this point we want to
** create a Name and add it to the NameTable in order to
** track the source of the link. This name (as all
** names) will be updated upon editing of the document.
*/
wsprintf(
szbuf,
"%s %ld",
(LPSTR)DEFRANGENAMEPREFIX,
++(lpServerDoc->m_nNextRangeNo)
);

lpOutlineName = OutlineApp_CreateName(lpOutlineApp);

if (lpOutlineName) {
A2W (szbuf, lpOutlineName->m_szName, OLEUI_CCHPATHMAX);
lpOutlineName->m_nStartLine = lplrSel->m_nStartLine;
lpOutlineName->m_nEndLine = lplrSel->m_nEndLine;
OutlineDoc_AddName(lpOutlineDoc, lpOutlineName);
} else {
// REVIEW: do we need "Out-of-Memory" error message here?
}
break;

case GETMONIKER_TEMPFORUSER:

/* Create a name to show to the user in the Paste
** Special dialog but do NOT yet incur the overhead
** of adding a Name to the NameTable. The Moniker
** generated should be useful to display to the user
** to indicate the source of the copy, but will NOT
** be used to create a link directly (the caller
** should ask again for a moniker specifying FORCEASSIGN).
** we will generate the name that would be the next
** auto-generated range name, BUT will NOT actually
** increment the range counter.
*/
wsprintf(
szbuf,
"%s %ld",
(LPSTR)DEFRANGENAMEPREFIX,
(lpServerDoc->m_nNextRangeNo)+1
);
break;

case GETMONIKER_ONLYIFTHERE:

/* the caller only wants a name if one has already been
** assigned. we have already above checked if the
** current selection has a name, so we will simply
** return NULL here.
*/
return NULL; // no moniker is assigned

default:
return NULL; // unknown flag given
}

A2W (szbuf, szUniBuf, MAXNAMESIZE);
CreateItemMoniker(OLESTR("\\"), szUniBuf, &lpmk);
}

return lpmk;
}


/* ServerDoc_GetSelFullMoniker
** ---------------------------
** Retrieve the full absolute moniker which identifies the given
** selection (lplrSel).
** this moniker is created as a composite of the absolute moniker for
** the entire document appended with an item moniker which identifies
** the selection relative to the document.
** Returns NULL if a moniker can NOT be created.
*/
LPMONIKER ServerDoc_GetSelFullMoniker(
LPSERVERDOC lpServerDoc,
LPLINERANGE lplrSel,
DWORD dwAssign
)
{
LPMONIKER lpmkDoc = NULL;
LPMONIKER lpmkItem = NULL;
LPMONIKER lpmkFull = NULL;

lpmkDoc = OleDoc_GetFullMoniker(
(LPOLEDOC)lpServerDoc,
dwAssign
);
if (! lpmkDoc) return NULL;

lpmkItem = ServerDoc_GetSelRelMoniker(
lpServerDoc,
lplrSel,
dwAssign
);
if (lpmkItem) {
CreateGenericComposite(lpmkDoc, lpmkItem, (LPMONIKER FAR*)&lpmkFull);
OleStdRelease((LPUNKNOWN)lpmkItem);
}

if (lpmkDoc)
OleStdRelease((LPUNKNOWN)lpmkDoc);

return lpmkFull;
}


/* ServerNameTable_EditLineUpdate
* -------------------------------
*
* Update the table when a line at nEditIndex is edited.
*/
void ServerNameTable_EditLineUpdate(
LPSERVERNAMETABLE lpServerNameTable,
int nEditIndex
)
{
LPOUTLINENAMETABLE lpOutlineNameTable =
(LPOUTLINENAMETABLE)lpServerNameTable;
LPOUTLINENAME lpOutlineName;
LINERANGE lrSel;
LPPSEUDOOBJ lpPseudoObj;
int i;

for(i = 0; i < lpOutlineNameTable->m_nCount; i++) {
lpOutlineName=OutlineNameTable_GetName(lpOutlineNameTable, i);

lpPseudoObj = ((LPSERVERNAME)lpOutlineName)->m_lpPseudoObj;

/* if there is a pseudo object associated with this name, then
** check if the line that was modified is included within
** the named range.
*/
if (lpPseudoObj) {
OutlineName_GetSel(lpOutlineName, &lrSel);

if(((int)lrSel.m_nStartLine <= nEditIndex) &&
((int)lrSel.m_nEndLine >= nEditIndex)) {

// inform linking clients data has changed
PseudoObj_SendAdvise(
lpPseudoObj,
OLE_ONDATACHANGE,
NULL, /* lpmkDoc -- not relevant here */
0 /* advf -- no flags necessary */
);
}

}
}
}


/* ServerNameTable_InformAllPseudoObjectsDocRenamed
* ------------------------------------------------
*
* Inform all pseudo object clients that the name of the pseudo
* object has changed.
*/
void ServerNameTable_InformAllPseudoObjectsDocRenamed(
LPSERVERNAMETABLE lpServerNameTable,
LPMONIKER lpmkDoc
)
{
LPOUTLINENAMETABLE lpOutlineNameTable =
(LPOUTLINENAMETABLE)lpServerNameTable;
LPOUTLINENAME lpOutlineName;
LPPSEUDOOBJ lpPseudoObj;
LPMONIKER lpmkObj;
int i;

OLEDBG_BEGIN2("ServerNameTable_InformAllPseudoObjectsDocRenamed\r\n");

for(i = 0; i < lpOutlineNameTable->m_nCount; i++) {
lpOutlineName=OutlineNameTable_GetName(lpOutlineNameTable, i);

lpPseudoObj = ((LPSERVERNAME)lpOutlineName)->m_lpPseudoObj;

/* if there is a pseudo object associated with this name, then
** send OnRename advise to its linking clients.
*/
if (lpPseudoObj &&
((lpmkObj=PseudoObj_GetFullMoniker(lpPseudoObj,lpmkDoc))!=NULL)) {

// inform the clients that the name has changed
PseudoObj_SendAdvise (
lpPseudoObj,
OLE_ONRENAME,
lpmkObj,
0 /* advf -- not relevant here */
);
}
}
OLEDBG_END2
}


/* ServerNameTable_InformAllPseudoObjectsDocSaved
* ------------------------------------------------
*
* Inform all pseudo object clients that the name of the pseudo
* object has changed.
*/
void ServerNameTable_InformAllPseudoObjectsDocSaved(
LPSERVERNAMETABLE lpServerNameTable,
LPMONIKER lpmkDoc
)
{
LPOUTLINENAMETABLE lpOutlineNameTable =
(LPOUTLINENAMETABLE)lpServerNameTable;
LPOUTLINENAME lpOutlineName;
LPPSEUDOOBJ lpPseudoObj;
LPMONIKER lpmkObj;
int i;

OLEDBG_BEGIN2("ServerNameTable_InformAllPseudoObjectsDocSaved\r\n");

for(i = 0; i < lpOutlineNameTable->m_nCount; i++) {
lpOutlineName=OutlineNameTable_GetName(lpOutlineNameTable, i);

lpPseudoObj = ((LPSERVERNAME)lpOutlineName)->m_lpPseudoObj;

/* if there is a pseudo object associated with this name, then
** send OnSave advise to its linking clients.
*/
if (lpPseudoObj &&
((lpmkObj=PseudoObj_GetFullMoniker(lpPseudoObj,lpmkDoc))!=NULL)) {

// inform the clients that the name has been saved
PseudoObj_SendAdvise (
lpPseudoObj,
OLE_ONSAVE,
NULL, /* lpmkDoc -- not relevant here */
0 /* advf -- not relevant here */
);
}
}
OLEDBG_END2
}


/* ServerNameTable_SendPendingAdvises
* ----------------------------------
*
* Send any pending change notifications for pseudo objects.
* while ReDraw is diabled on the ServerDoc, then change advise
* notifications are not sent to pseudo object clients.
*/
void ServerNameTable_SendPendingAdvises(LPSERVERNAMETABLE lpServerNameTable)
{
LPOUTLINENAMETABLE lpOutlineNameTable =
(LPOUTLINENAMETABLE)lpServerNameTable;
LPSERVERNAME lpServerName;
int i;

for(i = 0; i < lpOutlineNameTable->m_nCount; i++) {
lpServerName = (LPSERVERNAME)OutlineNameTable_GetName(
lpOutlineNameTable,
i
);
ServerName_SendPendingAdvises(lpServerName);
}
}


/* ServerNameTable_GetPseudoObj
** ----------------------------
**
** Return a pointer to a pseudo object identified by an item string
** (lpszItem). if the pseudo object already exists, then return the
** existing object, otherwise allocate a new pseudo object.
*/
LPPSEUDOOBJ ServerNameTable_GetPseudoObj(
LPSERVERNAMETABLE lpServerNameTable,
LPOLESTR lpszItem,
LPSERVERDOC lpServerDoc
)
{
LPSERVERNAME lpServerName;

lpServerName = (LPSERVERNAME)OutlineNameTable_FindName(
(LPOUTLINENAMETABLE)lpServerNameTable,
lpszItem
);

if (lpServerName)
return ServerName_GetPseudoObj(lpServerName, lpServerDoc);
else
return NULL;
}


/* ServerNameTable_CloseAllPseudoObjs
* ----------------------------------
*
* Force all pseudo objects to close. this results in sending OnClose
* notification to each pseudo object's linking clients.
*/
void ServerNameTable_CloseAllPseudoObjs(LPSERVERNAMETABLE lpServerNameTable)
{
LPOUTLINENAMETABLE lpOutlineNameTable =
(LPOUTLINENAMETABLE)lpServerNameTable;
LPSERVERNAME lpServerName;
int i;

OLEDBG_BEGIN3("ServerNameTable_CloseAllPseudoObjs\r\n")

for(i = 0; i < lpOutlineNameTable->m_nCount; i++) {
lpServerName = (LPSERVERNAME)OutlineNameTable_GetName(
lpOutlineNameTable,
i
);
ServerName_ClosePseudoObj(lpServerName);
}

OLEDBG_END3
}



/* ServerName_SetSel
* -----------------
*
* Change the line range of a name.
*/
void ServerName_SetSel(
LPSERVERNAME lpServerName,
LPLINERANGE lplrSel,
BOOL fRangeModified
)
{
LPOUTLINENAME lpOutlineName = (LPOUTLINENAME)lpServerName;
BOOL fPseudoObjChanged = fRangeModified;

if (lpOutlineName->m_nStartLine != lplrSel->m_nStartLine) {
lpOutlineName->m_nStartLine = lplrSel->m_nStartLine;
fPseudoObjChanged = TRUE;
}

if (lpOutlineName->m_nEndLine != lplrSel->m_nEndLine) {
lpOutlineName->m_nEndLine = lplrSel->m_nEndLine;
fPseudoObjChanged = TRUE;
}

/* NOTE: if the range of an active pseudo object has
** changed, then inform any linking clients that the object
** has changed.
*/
if (lpServerName->m_lpPseudoObj && fPseudoObjChanged) {
PseudoObj_SendAdvise(
lpServerName->m_lpPseudoObj,
OLE_ONDATACHANGE,
NULL, /* lpmkDoc -- not relevant here */
0 /* advf -- no flags necessary */
);
}
}


/* ServerName_SendPendingAdvises
* -----------------------------
*
* Send any pending change notifications for the associated
* pseudo objects for this name (if one exists).
* while ReDraw is diabled on the ServerDoc, then change advise
* notifications are not sent to pseudo object clients.
*/
void ServerName_SendPendingAdvises(LPSERVERNAME lpServerName)
{
if (! lpServerName->m_lpPseudoObj)
return; // no associated pseudo object

if (lpServerName->m_lpPseudoObj->m_fDataChanged)
PseudoObj_SendAdvise(
lpServerName->m_lpPseudoObj,
OLE_ONDATACHANGE,
NULL, /* lpmkDoc -- not relevant here */
0 /* advf -- no flags necessary */
);
}


/* ServerName_GetPseudoObj
** -----------------------
**
** Return a pointer to a pseudo object associated to a ServerName.
** if the pseudo object already exists, then return the
** existing object, otherwise allocate a new pseudo object.
**
** NOTE: the PseudoObj is returned with a 0 refcnt if first created,
** else the existing refcnt is unchanged.
*/
LPPSEUDOOBJ ServerName_GetPseudoObj(
LPSERVERNAME lpServerName,
LPSERVERDOC lpServerDoc
)
{
// Check if a PseudoObj already exists
if (lpServerName->m_lpPseudoObj)
return lpServerName->m_lpPseudoObj;

// A PseudoObj does NOT already exist, allocate a new one.
lpServerName->m_lpPseudoObj=(LPPSEUDOOBJ) New((DWORD)sizeof(PSEUDOOBJ));
if (lpServerName->m_lpPseudoObj == NULL) {
OleDbgAssertSz(lpServerName->m_lpPseudoObj != NULL, "Error allocating PseudoObj");
return NULL;
}

PseudoObj_Init(lpServerName->m_lpPseudoObj, lpServerName, lpServerDoc);
return lpServerName->m_lpPseudoObj;
}


/* ServerName_ClosePseudoObj
* -------------------------
*
* if there is an associated pseudo objects for this name (if one
* exists), then close it. this results in sending OnClose
* notification to the pseudo object's linking clients.
*/
void ServerName_ClosePseudoObj(LPSERVERNAME lpServerName)
{
if (!lpServerName || !lpServerName->m_lpPseudoObj)
return; // no associated pseudo object

PseudoObj_Close(lpServerName->m_lpPseudoObj);
}


#endif // OLE_SERVER


#if defined( OLE_CNTR )


/*************************************************************************
** ContainerDoc Supprt Functions Used by Container versions
*************************************************************************/


/* ContainerLine_GetRelMoniker
** ---------------------------
** Retrieve the relative item moniker which identifies the OLE object
** relative to the container document.
**
** Returns NULL if a moniker can NOT be created.
*/
LPMONIKER ContainerLine_GetRelMoniker(
LPCONTAINERLINE lpContainerLine,
DWORD dwAssign
)
{
LPMONIKER lpmk = NULL;

/* NOTE: we should only give out a moniker for the OLE object
** if the object is allowed to be linked to from the inside. if
** so we are allowed to give out a moniker which binds to the
** running OLE object). if the object is an OLE 2.0 embedded
** object then it is allowed to be linked to from the inside. if
** the object is either an OleLink or an OLE 1.0 embedding
** then it can not be linked to from the inside.
** if we were a container/server app then we could offer linking
** to the outside of the object (ie. a pseudo object within our
** document). we are a container only app that does not support
** linking to ranges of its data.
*/

switch (dwAssign) {

case GETMONIKER_FORCEASSIGN:

/* Force the assignment of the name. This is called when a
** Paste Link actually occurs. From now on we want
** to inform the OLE object that its moniker is
** assigned and is thus necessary to register itself
** in the RunningObjectTable.
*/
CreateItemMoniker(
OLESTR("\\"), lpContainerLine->m_szStgName, &lpmk);

/* NOTE: if the OLE object is already loaded and it
** is being assigned a moniker for the first time,
** then we need to inform it that it now has a moniker
** assigned by calling IOleObject::SetMoniker. this
** will force the OLE object to register in the
** RunningObjectTable when it enters the running
** state. if the object is not currently loaded,
** SetMoniker will be called automatically later when
** the object is loaded by the function
** ContainerLine_LoadOleObject.
*/
if (! lpContainerLine->m_fMonikerAssigned) {

/* we must remember forever more that this object has a
** moniker assigned.
*/
lpContainerLine->m_fMonikerAssigned = TRUE;

// we are now dirty and must be saved
OutlineDoc_SetModified(
(LPOUTLINEDOC)lpContainerLine->m_lpDoc,
TRUE, /* fModified */
FALSE, /* fDataChanged--N/A for container ver. */
FALSE /* fSizeChanged--N/A for container ver. */
);

if (lpContainerLine->m_lpOleObj) {
OLEDBG_BEGIN2("IOleObject::SetMoniker called\r\n")
lpContainerLine->m_lpOleObj->lpVtbl->SetMoniker(
lpContainerLine->m_lpOleObj,
OLEWHICHMK_OBJREL,
lpmk
);
OLEDBG_END2
}
}
break;

case GETMONIKER_ONLYIFTHERE:

/* If the OLE object currently has a moniker assigned,
** then return it.
*/
if (lpContainerLine->m_fMonikerAssigned) {
CreateItemMoniker(
OLESTR("\\"),
lpContainerLine->m_szStgName,
&lpmk
);
}
break;

case GETMONIKER_TEMPFORUSER:

/* Return the moniker that would be used for the OLE
** object but do NOT force moniker assignment at
** this point. Since our strategy is to use the
** storage name of the object as its item name, we
** can simply create the corresponding ItemMoniker
** (indepenedent of whether the moniker is currently
** assigned or not).
*/
CreateItemMoniker(
OLESTR("\\"),
lpContainerLine->m_szStgName,
&lpmk
);
break;

case GETMONIKER_UNASSIGN:

lpContainerLine->m_fMonikerAssigned = FALSE;
break;

}

return lpmk;
}


/* ContainerLine_GetFullMoniker
** ----------------------------
** Retrieve the full absolute moniker which identifies the OLE object
** in the container document.
** this moniker is created as a composite of the absolute moniker for
** the entire document appended with an item moniker which identifies
** the OLE object relative to the document.
** Returns NULL if a moniker can NOT be created.
*/
LPMONIKER ContainerLine_GetFullMoniker(
LPCONTAINERLINE lpContainerLine,
DWORD dwAssign
)
{
LPMONIKER lpmkDoc = NULL;
LPMONIKER lpmkItem = NULL;
LPMONIKER lpmkFull = NULL;

lpmkDoc = OleDoc_GetFullMoniker(
(LPOLEDOC)lpContainerLine->m_lpDoc,
dwAssign
);
if (! lpmkDoc)
{
return NULL;
}
lpmkItem = ContainerLine_GetRelMoniker(lpContainerLine, dwAssign);

if (lpmkItem) {
CreateGenericComposite(lpmkDoc, lpmkItem, (LPMONIKER FAR*)&lpmkFull);
OleStdRelease((LPUNKNOWN)lpmkItem);
}

if (lpmkDoc)
OleStdRelease((LPUNKNOWN)lpmkDoc);

return lpmkFull;
}


/* ContainerDoc_InformAllOleObjectsDocRenamed
** ------------------------------------------
** Inform all OLE objects that the name of the ContainerDoc has changed.
*/
void ContainerDoc_InformAllOleObjectsDocRenamed(
LPCONTAINERDOC lpContainerDoc,
LPMONIKER lpmkDoc
)
{
LPLINELIST lpLL = &((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
int i;
LPLINE lpLine;

// artificial AddRef in case someone releases object during call
OleDoc_AddRef((LPOLEDOC)lpContainerDoc);

for (i = 0; i < lpLL->m_nNumLines; i++) {
lpLine=LineList_GetLine(lpLL, i);

if (lpLine && (Line_GetLineType(lpLine)==CONTAINERLINETYPE)) {
LPCONTAINERLINE lpContainerLine = (LPCONTAINERLINE)lpLine;

/* NOTE: if the OLE object is already loaded AND the
** object already has a moniker assigned, then we need
** to inform it that the moniker of the ContainerDoc has
** changed. of course, this means the full moniker of
** the object has changed. to do this we call
** IOleObject::SetMoniker. this will force the OLE
** object to re-register in the RunningObjectTable if it
** is currently in the running state. it is not in the
** running state, the object handler can make not that
** the object has a new moniker. if the object is not
** currently loaded, SetMoniker will be called
** automatically later when the object is loaded by the
** function ContainerLine_LoadOleObject.
** also if the object is a linked object, we always want
** to call SetMoniker on the link so that in case the
** link source is contained within our same container,
** the link source will be tracked. the link rebuilds
** its absolute moniker if it has a relative moniker.
*/
if (lpContainerLine->m_lpOleObj) {
if (lpContainerLine->m_fMonikerAssigned ||
lpContainerLine->m_dwLinkType != 0) {
OLEDBG_BEGIN2("IOleObject::SetMoniker called\r\n")
lpContainerLine->m_lpOleObj->lpVtbl->SetMoniker(
lpContainerLine->m_lpOleObj,
OLEWHICHMK_CONTAINER,
lpmkDoc
);
OLEDBG_END2
}

/* NOTE: we must call IOleObject::SetHostNames so
** any open objects can update their window titles.
*/
OLEDBG_BEGIN2("IOleObject::SetHostNames called\r\n")
lpContainerLine->m_lpOleObj->lpVtbl->SetHostNames(
lpContainerLine->m_lpOleObj,
/*(LPOLESTR)*/APPNAME,
((LPOUTLINEDOC)lpContainerDoc)->m_lpszDocTitle
);
OLEDBG_END2
}
}
}
// release artificial AddRef
OleDoc_Release((LPOLEDOC)lpContainerDoc);
}


/* ContainerDoc_GetObject
** ----------------------
** Return a pointer to the desired interface of an object identified
** by an item string (lpszItem). the object returned will be an OLE
** object (either link or embedding).
**
** NOTE: we must force the object to run because we are
** REQUIRED to return a pointer the OLE object in the
** RUNNING state.
**
** dwSpeedNeeded indicates how long the caller is willing
** to wait for us to get the object:
** BINDSPEED_IMMEDIATE -- only if obj already loaded && IsRunning
** BINDSPEED_MODERATE -- load obj if necessary && if IsRunning
** BINDSPEED_INDEFINITE-- force obj to load and run if necessary
*/
HRESULT ContainerDoc_GetObject(
LPCONTAINERDOC lpContainerDoc,
LPOLESTR lpszItem,
DWORD dwSpeedNeeded,
REFIID riid,
LPVOID FAR* lplpvObject
)
{
LPLINELIST lpLL = &((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
int i;
LPLINE lpLine;
BOOL fMatchFound = FALSE;
DWORD dwStatus;
HRESULT hrErr;

*lplpvObject = NULL;

for (i = 0; i < lpLL->m_nNumLines; i++) {
lpLine=LineList_GetLine(lpLL, i);

if (lpLine && (Line_GetLineType(lpLine)==CONTAINERLINETYPE)) {
LPCONTAINERLINE lpContainerLine = (LPCONTAINERLINE)lpLine;

if (OLESTRCMP(lpContainerLine->m_szStgName, lpszItem) == 0) {

fMatchFound = TRUE; // valid item name

// check if object is loaded.
if (lpContainerLine->m_lpOleObj == NULL) {

// if BINDSPEED_IMMEDIATE is requested, object must
// ALREADY be loadded.
if (dwSpeedNeeded == BINDSPEED_IMMEDIATE)
return MK_E_EXCEEDEDDEADLINE;

ContainerLine_LoadOleObject(lpContainerLine);
if (! lpContainerLine->m_lpOleObj)
return E_OUTOFMEMORY;
}

/* NOTE: check if the object is allowed to be linked
** to from the inside (ie. we are allowed to
** give out a moniker which binds to the running
** OLE object). if the object is an OLE
** 2.0 embedded object then it is allowed to be
** linked to from the inside. if the object is
** either an OleLink or an OLE 1.0 embedding
** then it can not be linked to from the inside.
** if we were a container/server app then we
** could offer linking to the outside of the
** object (ie. a pseudo object within our
** document). we are a container only app that
** does not support linking to ranges of its data.
*/
OLEDBG_BEGIN2("IOleObject::GetMiscStatus called\r\n");
lpContainerLine->m_lpOleObj->lpVtbl->GetMiscStatus(
lpContainerLine->m_lpOleObj,
DVASPECT_CONTENT, /* aspect is not important */
(LPDWORD)&dwStatus
);
OLEDBG_END2
if (dwStatus & OLEMISC_CANTLINKINSIDE)
{
return MK_E_NOOBJECT;
}

// check if object is running.
if (! OleIsRunning(lpContainerLine->m_lpOleObj)) {

// if BINDSPEED_MODERATE is requested, object must
// ALREADY be running.
if (dwSpeedNeeded == BINDSPEED_MODERATE)
{
return MK_E_EXCEEDEDDEADLINE;
}

/* NOTE: we have found a match for the item name.
** now we must return a pointer to the desired
** interface on the RUNNING object. we must
** carefully load the object and initially ask for
** an interface that we are sure the loaded form of
** the object supports. if we immediately ask the
** loaded object for the desired interface, the
** QueryInterface call might fail if it is an
** interface that is supported only when the object
** is running. thus we force the object to load and
** return its IUnknown*. then we force the object to
** run, and then finally, we can ask for the
** actually requested interface.
*/
hrErr = ContainerLine_RunOleObject(lpContainerLine);
if (hrErr != NOERROR) {
return hrErr;
}
}

// Retrieve the requested interface
*lplpvObject = OleStdQueryInterface(
(LPUNKNOWN)lpContainerLine->m_lpOleObj, riid);

break; // Match FOUND!
}
}
}

if (*lplpvObject != NULL) {
return NOERROR;
} else
{
return (fMatchFound ? E_NOINTERFACE
: MK_E_NOOBJECT);
}
}


/* ContainerDoc_GetObjectStorage

** ----------------------------- 
** Return a pointer to the IStorage* used by the object identified
** by an item string (lpszItem). the object identified could be either
** an OLE object (either link or embedding).
*/
HRESULT ContainerDoc_GetObjectStorage(
LPCONTAINERDOC lpContainerDoc,
LPOLESTR lpszItem,
LPSTORAGE FAR* lplpStg
)
{
LPLINELIST lpLL = &((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
int i;
LPLINE lpLine;

*lplpStg = NULL;

for (i = 0; i < lpLL->m_nNumLines; i++) {
lpLine=LineList_GetLine(lpLL, i);

if (lpLine && (Line_GetLineType(lpLine)==CONTAINERLINETYPE)) {
LPCONTAINERLINE lpContainerLine = (LPCONTAINERLINE)lpLine;

if (OLESTRCMP(lpContainerLine->m_szStgName, lpszItem) == 0) {

*lplpStg = lpContainerLine->m_lpStg;
break; // Match FOUND!
}
}
}

if (*lplpStg != NULL) {
return NOERROR;
} else
{
return MK_E_NOOBJECT;
}
}


/* ContainerDoc_IsRunning
** ----------------------
** Check if the object identified by an item string (lpszItem) is in
** the running state.
** For a container-only app, a check is made if the OLE object
** associated with the item name is running.
*/
HRESULT ContainerDoc_IsRunning(LPCONTAINERDOC lpContainerDoc, LPOLESTR lpszItem)
{
LPLINELIST lpLL = &((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
int i;
LPLINE lpLine;
DWORD dwStatus;

for (i = 0; i < lpLL->m_nNumLines; i++) {
lpLine=LineList_GetLine(lpLL, i);

if (lpLine && (Line_GetLineType(lpLine)==CONTAINERLINETYPE)) {
LPCONTAINERLINE lpContainerLine = (LPCONTAINERLINE)lpLine;

if (OLESTRCPY(lpContainerLine->m_szStgName, lpszItem) == 0) {

/* NOTE: we have found a match for the item name.
** now we must check if the OLE object is running.
** we will load the object if not already loaded.
*/
if (! lpContainerLine->m_lpOleObj) {
ContainerLine_LoadOleObject(lpContainerLine);
if (! lpContainerLine->m_lpOleObj)
return E_OUTOFMEMORY;
}

/* NOTE: check if the object is allowed to be linked
** to from the inside (ie. we are allowed to
** give out a moniker which binds to the running
** OLE object). if the object is an OLE
** 2.0 embedded object then it is allowed to be
** linked to from the inside. if the object is
** either an OleLink or an OLE 1.0 embedding
** then it can not be linked to from the inside.
** if we were a container/server app then we
** could offer linking to the outside of the
** object (ie. a pseudo object within our
** document). we are a container only app that
** does not support linking to ranges of its data.
*/
OLEDBG_BEGIN2("IOleObject::GetMiscStatus called\r\n")
lpContainerLine->m_lpOleObj->lpVtbl->GetMiscStatus(
lpContainerLine->m_lpOleObj,
DVASPECT_CONTENT, /* aspect is not important */
(LPDWORD)&dwStatus
);
OLEDBG_END2
if (dwStatus & OLEMISC_CANTLINKINSIDE)
{
return MK_E_NOOBJECT;
}

if (OleIsRunning(lpContainerLine->m_lpOleObj))
{
return NOERROR;
}
else
{
return S_FALSE;
}
}
}
}
// no object was found corresponding to the item name
return MK_E_NOOBJECT;
}

#endif // OLE_CNTR