/*************************************************************************
**
**    OLE 2 Server Sample Code
**
**    svrpsobj.c
**
**    This file contains all PseudoObj methods and related support
**    functions.
**
**    (c) Copyright Microsoft Corp. 1992 - 1993 All Rights Reserved
**
*************************************************************************/


#include "outline.h"

OLEDBGDATA

extern LPOUTLINEAPP             g_lpApp;
extern IUnknownVtbl             g_PseudoObj_UnknownVtbl;
extern IOleObjectVtbl           g_PseudoObj_OleObjectVtbl;
extern IDataObjectVtbl          g_PseudoObj_DataObjectVtbl;


/* PseudoObj_Init
** --------------
**  Initialize fields in a newly constructed PseudoObj.
**  NOTE: ref cnt of PseudoObj initialized to 0
*/
void PseudoObj_Init(
		LPPSEUDOOBJ             lpPseudoObj,
		LPSERVERNAME            lpServerName,
		LPSERVERDOC             lpServerDoc
)
{
	OleDbgOut2("++PseudoObj Created\r\n");

	lpPseudoObj->m_cRef             = 0;
	lpPseudoObj->m_lpName           = lpServerName;
	lpPseudoObj->m_lpDoc            = lpServerDoc;
	lpPseudoObj->m_lpOleAdviseHldr  = NULL;
	lpPseudoObj->m_lpDataAdviseHldr = NULL;
	lpPseudoObj->m_fObjIsClosing    = FALSE;

	INIT_INTERFACEIMPL(
			&lpPseudoObj->m_Unknown,
			&g_PseudoObj_UnknownVtbl,
			lpPseudoObj
	);

	INIT_INTERFACEIMPL(
			&lpPseudoObj->m_OleObject,
			&g_PseudoObj_OleObjectVtbl,
			lpPseudoObj
	);

	INIT_INTERFACEIMPL(
			&lpPseudoObj->m_DataObject,
			&g_PseudoObj_DataObjectVtbl,
			lpPseudoObj
	);

	/* OLE2NOTE: Increment the refcnt of the Doc on behalf of the
	**    PseudoObj. the Document should not shut down unless all
	**    pseudo objects are closed. when a pseudo object is destroyed,
	**    it calls ServerDoc_PseudoObjUnlockDoc to release this hold on
	**    the document.
	*/
	ServerDoc_PseudoObjLockDoc(lpServerDoc);
}



/* PseudoObj_AddRef
** ----------------
**
**  increment the ref count of the PseudoObj object.
**
**    Returns the new ref count on the object
*/
ULONG PseudoObj_AddRef(LPPSEUDOOBJ lpPseudoObj)
{
	++lpPseudoObj->m_cRef;

#if defined( _DEBUG )
	OleDbgOutRefCnt4(
			"PseudoObj_AddRef: cRef++\r\n",
			lpPseudoObj,
			lpPseudoObj->m_cRef
	);
#endif
	return lpPseudoObj->m_cRef;
}


/* PseudoObj_Release
** -----------------
**
**  decrement the ref count of the PseudoObj object.
**    if the ref count goes to 0, then the PseudoObj is destroyed.
**
**    Returns the remaining ref count on the object
*/
ULONG PseudoObj_Release(LPPSEUDOOBJ lpPseudoObj)
{
	ULONG cRef;

	/*********************************************************************
	** OLE2NOTE: when the obj refcnt == 0, then destroy the object.     **
	**     otherwise the object is still in use.                        **
	*********************************************************************/

	cRef = --lpPseudoObj->m_cRef;

#if defined( _DEBUG )
	OleDbgAssertSz(lpPseudoObj->m_cRef >= 0,"Release called with cRef == 0");

	OleDbgOutRefCnt4(
			"PseudoObj_Release: cRef--\r\n", lpPseudoObj,cRef);
#endif

	if (cRef == 0)
		PseudoObj_Destroy(lpPseudoObj);

	return cRef;
}


/* PseudoObj_QueryInterface
** ------------------------
**
** Retrieve a pointer to an interface on the PseudoObj object.
**
**    Returns S_OK if interface is successfully retrieved.
**            E_NOINTERFACE if the interface is not supported
*/
HRESULT PseudoObj_QueryInterface(
		LPPSEUDOOBJ         lpPseudoObj,
		REFIID              riid,
		LPVOID FAR*         lplpvObj
)
{
	SCODE sc = E_NOINTERFACE;

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

	if (IsEqualIID(riid, &IID_IUnknown)) {
		OleDbgOut4("PseudoObj_QueryInterface: IUnknown* RETURNED\r\n");

		*lplpvObj = (LPVOID) &lpPseudoObj->m_Unknown;
		PseudoObj_AddRef(lpPseudoObj);
		sc = S_OK;
	}
	else if (IsEqualIID(riid, &IID_IOleObject)) {
		OleDbgOut4("PseudoObj_QueryInterface: IOleObject* RETURNED\r\n");

		*lplpvObj = (LPVOID) &lpPseudoObj->m_OleObject;
		PseudoObj_AddRef(lpPseudoObj);
		sc = S_OK;
	}
	else if (IsEqualIID(riid, &IID_IDataObject)) {
		OleDbgOut4("PseudoObj_QueryInterface: IDataObject* RETURNED\r\n");

		*lplpvObj = (LPVOID) &lpPseudoObj->m_DataObject;
		PseudoObj_AddRef(lpPseudoObj);
		sc = S_OK;
	}

	OleDbgQueryInterfaceMethod(*lplpvObj);

	return ResultFromScode(sc);
}


/* PseudoObj_Close
 * ---------------
 *
 *  Close the pseudo object. Force all external connections to close
 *      down. This causes link clients to release this PseudoObj. when
 *      the refcount actually reaches 0, then the PseudoObj will be
 *      destroyed.
 *
 *  Returns:
 *      FALSE -- user canceled the closing of the doc.
 *      TRUE -- the doc was successfully closed
 */

BOOL PseudoObj_Close(LPPSEUDOOBJ lpPseudoObj)
{
	LPSERVERDOC lpServerDoc = (LPSERVERDOC)lpPseudoObj->m_lpDoc;
	LPSERVERNAME lpServerName = (LPSERVERNAME)lpPseudoObj->m_lpName;
	LPOLEAPP lpOleApp = (LPOLEAPP)g_lpApp;
	LPOLEDOC lpOleDoc = (LPOLEDOC)lpServerDoc;
	BOOL fStatus = TRUE;

	if (lpPseudoObj->m_fObjIsClosing)
		return TRUE;    // Closing is already in progress

	lpPseudoObj->m_fObjIsClosing = TRUE;   // guard against recursive call

	OLEDBG_BEGIN3("PseudoObj_Close\r\n")

	/* OLE2NOTE: in order to have a stable App, Doc, AND pseudo object
	**    during the process of closing, we intially AddRef the App,
	**    Doc, and PseudoObj ref counts and later Release them. These
	**    initial AddRefs are artificial; they are simply done to
	**    guarantee that these objects do not get destroyed until the
	**    end of this routine.
	*/
	OleApp_AddRef(lpOleApp);
	OleDoc_AddRef(lpOleDoc);
	PseudoObj_AddRef(lpPseudoObj);

	if (lpPseudoObj->m_lpDataAdviseHldr) {
		/* OLE2NOTE: send last OnDataChange notification to clients
		**    that have registered for data notifications when object
		**    stops running (ADVF_DATAONSTOP)
		*/
		PseudoObj_SendAdvise(
				lpPseudoObj,
				OLE_ONDATACHANGE,
				NULL,   /* lpmkObj -- not relevant here */
				ADVF_DATAONSTOP
		);

		/* OLE2NOTE: we just sent the last data notification that we
		**    need to send; release our DataAdviseHolder. we SHOULD be
		**    the only one using it.
		*/
		OleStdVerifyRelease(
				(LPUNKNOWN)lpPseudoObj->m_lpDataAdviseHldr,
				"DataAdviseHldr not released properly"
		);
		lpPseudoObj->m_lpDataAdviseHldr = NULL;
	}

	if (lpPseudoObj->m_lpOleAdviseHldr) {
		// OLE2NOTE: inform all of our linking clients that we are closing.
		PseudoObj_SendAdvise(
				lpPseudoObj,
				OLE_ONCLOSE,
				NULL,   /* lpmkObj -- not relevant here */
				0       /* advf -- not relevant here */
		);

		/* OLE2NOTE: OnClose is the last notification that we need to
		**    send; release our OleAdviseHolder. we SHOULD be the only
		**    one using it. this will make our destructor realize that
		**    OnClose notification has already been sent.
		*/
		OleStdVerifyRelease(
				(LPUNKNOWN)lpPseudoObj->m_lpOleAdviseHldr,
				"OleAdviseHldr not released properly"
		);
		lpPseudoObj->m_lpOleAdviseHldr = NULL;
	}

	/* OLE2NOTE: this call forces all external connections to our
	**    object to close down and therefore guarantees that we receive
	**    all releases associated with those external connections.
	*/
	OLEDBG_BEGIN2("CoDisconnectObject called\r\n")
	CoDisconnectObject((LPUNKNOWN)&lpPseudoObj->m_Unknown, 0);
	OLEDBG_END2

	PseudoObj_Release(lpPseudoObj);     // release artificial AddRef above
	OleDoc_Release(lpOleDoc);           // release artificial AddRef above
	OleApp_Release(lpOleApp);           // release artificial AddRef above

	OLEDBG_END3
	return fStatus;
}


/* PseudoObj_Destroy
** -----------------
**    Destroy (Free) the memory used by a PseudoObj structure.
**    This function is called when the ref count of the PseudoObj goes
**    to zero. the ref cnt goes to zero after PseudoObj_Delete forces
**    the OleObject to unload and release its pointers to the
**    PseudoObj IOleClientSite and IAdviseSink interfaces.
*/

void PseudoObj_Destroy(LPPSEUDOOBJ lpPseudoObj)
{
	LPSERVERDOC lpServerDoc = lpPseudoObj->m_lpDoc;
	LPOLEAPP    lpOleApp = (LPOLEAPP)g_lpApp;
	LPOLEDOC    lpOleDoc = (LPOLEDOC)lpServerDoc;

	OLEDBG_BEGIN3("PseudoObj_Destroy\r\n")

	/* OLE2NOTE: in order to have a stable App, Doc, AND pseudo object
	**    during the process of closing, we intially AddRef the App,
	**    Doc ref counts and later Release them. These
	**    initial AddRefs are artificial; they are simply done to
	**    guarantee that these objects do not get destroyed until the
	**    end of this routine.
	*/
	OleApp_AddRef(lpOleApp);
	OleDoc_AddRef(lpOleDoc);

	/******************************************************************
	** OLE2NOTE: we no longer need the advise and enum holder objects,
	**    so release them.
	******************************************************************/

	if (lpPseudoObj->m_lpDataAdviseHldr) {
		/* release DataAdviseHldr; we SHOULD be the only one using it. */
		OleStdVerifyRelease(
				(LPUNKNOWN)lpPseudoObj->m_lpDataAdviseHldr,
				"DataAdviseHldr not released properly"
			);
		lpPseudoObj->m_lpDataAdviseHldr = NULL;
	}

	if (lpPseudoObj->m_lpOleAdviseHldr) {
		/* release OleAdviseHldr; we SHOULD be the only one using it. */
		OleStdVerifyRelease(
				(LPUNKNOWN)lpPseudoObj->m_lpOleAdviseHldr,
				"OleAdviseHldr not released properly"
			);
		lpPseudoObj->m_lpOleAdviseHldr = NULL;
	}

	/* forget the pointer to destroyed PseudoObj in NameTable */
	if (lpPseudoObj->m_lpName)
		lpPseudoObj->m_lpName->m_lpPseudoObj = NULL;

	/* OLE2NOTE: release the lock on the Doc held on behalf of the
	**    PseudoObj. the Document should not shut down unless all
	**    pseudo objects are closed. when a pseudo object is first
	**    created, it calls ServerDoc_PseudoObjLockDoc to guarantee
	**    that the document stays alive (called from PseudoObj_Init).
	*/
	ServerDoc_PseudoObjUnlockDoc(lpServerDoc, lpPseudoObj);

	Delete(lpPseudoObj);        // Free the memory for the structure itself

	OleDoc_Release(lpOleDoc);       // release artificial AddRef above
	OleApp_Release(lpOleApp);       // release artificial AddRef above

	OLEDBG_END3
}


/* PseudoObj_GetSel
** ----------------
**    Return the line range for the pseudo object
*/
void PseudoObj_GetSel(LPPSEUDOOBJ lpPseudoObj, LPLINERANGE lplrSel)
{
	LPOUTLINENAME lpOutlineName = (LPOUTLINENAME)lpPseudoObj->m_lpName;
	lplrSel->m_nStartLine = lpOutlineName->m_nStartLine;
	lplrSel->m_nEndLine = lpOutlineName->m_nEndLine;
}


/* PseudoObj_GetExtent
 * -------------------
 *
 *      Get the extent (width, height) of the entire document.
 */
void PseudoObj_GetExtent(LPPSEUDOOBJ lpPseudoObj, LPSIZEL lpsizel)
{
	LPOLEDOC lpOleDoc = (LPOLEDOC)lpPseudoObj->m_lpDoc;
	LPLINELIST lpLL = (LPLINELIST)&((LPOUTLINEDOC)lpOleDoc)->m_LineList;
	LINERANGE lrSel;

	PseudoObj_GetSel(lpPseudoObj, (LPLINERANGE)&lrSel);

	LineList_CalcSelExtentInHimetric(lpLL, (LPLINERANGE)&lrSel, lpsizel);
}


/* PseudoObj_SendAdvise
 * --------------------
 *
 * This function sends an advise notification on behalf of a specific
 *  doc object to all its clients.
 */
void PseudoObj_SendAdvise(
		LPPSEUDOOBJ lpPseudoObj,
		WORD        wAdvise,
		LPMONIKER   lpmkObj,
		DWORD       dwAdvf
)
{
	LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpPseudoObj->m_lpDoc;

	switch (wAdvise) {

		case OLE_ONDATACHANGE:

			// inform clients that the data of the object has changed

			if (lpOutlineDoc->m_nDisableDraw == 0) {
				/* drawing is currently enabled. inform clients that
				**    the data of the object has changed
				*/

				lpPseudoObj->m_fDataChanged = FALSE;
				if (lpPseudoObj->m_lpDataAdviseHldr) {

					OLEDBG_BEGIN2("IDataAdviseHolder::SendOnDataChange called\r\n");
					lpPseudoObj->m_lpDataAdviseHldr->lpVtbl->SendOnDataChange(
							lpPseudoObj->m_lpDataAdviseHldr,
							(LPDATAOBJECT)&lpPseudoObj->m_DataObject,
							0,
							dwAdvf
					);
					OLEDBG_END2
				}

			} else {
				/* drawing is currently disabled. do not send
				**    notifications until drawing is re-enabled.
				*/
				lpPseudoObj->m_fDataChanged = TRUE;
			}
			break;

		case OLE_ONCLOSE:

			// inform clients that the object is shutting down

			if (lpPseudoObj->m_lpOleAdviseHldr) {

				OLEDBG_BEGIN2("IOleAdviseHolder::SendOnClose called\r\n");
				lpPseudoObj->m_lpOleAdviseHldr->lpVtbl->SendOnClose(
						lpPseudoObj->m_lpOleAdviseHldr
				);
				OLEDBG_END2
			}
			break;

		case OLE_ONSAVE:

			// inform clients that the object has been saved

			if (lpPseudoObj->m_lpOleAdviseHldr) {

				OLEDBG_BEGIN2("IOleAdviseHolder::SendOnClose called\r\n");
				lpPseudoObj->m_lpOleAdviseHldr->lpVtbl->SendOnSave(
						lpPseudoObj->m_lpOleAdviseHldr
				);
				OLEDBG_END2
			}
			break;

		case OLE_ONRENAME:

			// inform clients that the object's name has changed
			if (lpmkObj && lpPseudoObj->m_lpOleAdviseHldr) {

				OLEDBG_BEGIN2("IOleAdviseHolder::SendOnRename called\r\n");
				if (lpPseudoObj->m_lpOleAdviseHldr)
					lpPseudoObj->m_lpOleAdviseHldr->lpVtbl->SendOnRename(
							lpPseudoObj->m_lpOleAdviseHldr,
							lpmkObj
					);
				OLEDBG_END2
			}
			break;
	}
}


/* PseudoObj_GetFullMoniker
 * ------------------------
 *
 * Returns the Full, absolute Moniker which identifies this pseudo object.
 */
LPMONIKER PseudoObj_GetFullMoniker(LPPSEUDOOBJ lpPseudoObj, LPMONIKER lpmkDoc)
{
	LPOUTLINENAME lpOutlineName = (LPOUTLINENAME)lpPseudoObj->m_lpName;
	LPMONIKER lpmkItem = NULL;
	LPMONIKER lpmkPseudoObj = NULL;

	if (lpmkDoc != NULL) {
		CreateItemMonikerA(OLESTDDELIM,lpOutlineName->m_szName,&lpmkItem);

		/* OLE2NOTE: create an absolute moniker which identifies the
		**    pseudo object. this moniker is created as a composite of
		**    the absolute moniker for the entire document appended
		**    with an item moniker which identifies the selection of
		**    the pseudo object relative to the document.
		*/
		CreateGenericComposite(lpmkDoc, lpmkItem, &lpmkPseudoObj);

		if (lpmkItem)
			OleStdRelease((LPUNKNOWN)lpmkItem);

		return lpmkPseudoObj;
	} else {
		return NULL;
	}
}


/*************************************************************************
** PseudoObj::IUnknown interface implementation
*************************************************************************/

STDMETHODIMP PseudoObj_Unk_QueryInterface(
		LPUNKNOWN         lpThis,
		REFIID            riid,
		LPVOID FAR*       lplpvObj
)
{
	LPPSEUDOOBJ lpPseudoObj =
			((struct CPseudoObjUnknownImpl FAR*)lpThis)->lpPseudoObj;

	return PseudoObj_QueryInterface(lpPseudoObj, riid, lplpvObj);
}


STDMETHODIMP_(ULONG) PseudoObj_Unk_AddRef(LPUNKNOWN lpThis)
{
	LPPSEUDOOBJ lpPseudoObj =
			((struct CPseudoObjUnknownImpl FAR*)lpThis)->lpPseudoObj;

	OleDbgAddRefMethod(lpThis, "IUnknown");

	return PseudoObj_AddRef(lpPseudoObj);
}


STDMETHODIMP_(ULONG) PseudoObj_Unk_Release (LPUNKNOWN lpThis)
{
	LPPSEUDOOBJ lpPseudoObj =
			((struct CPseudoObjUnknownImpl FAR*)lpThis)->lpPseudoObj;

	OleDbgReleaseMethod(lpThis, "IUnknown");

	return PseudoObj_Release(lpPseudoObj);
}


/*************************************************************************
** PseudoObj::IOleObject interface implementation
*************************************************************************/

STDMETHODIMP PseudoObj_OleObj_QueryInterface(
		LPOLEOBJECT     lpThis,
		REFIID          riid,
		LPVOID FAR*     lplpvObj
)
{
	LPPSEUDOOBJ lpPseudoObj =
			((struct CPseudoObjOleObjectImpl FAR*)lpThis)->lpPseudoObj;

	return PseudoObj_QueryInterface(lpPseudoObj, riid, lplpvObj);
}


STDMETHODIMP_(ULONG) PseudoObj_OleObj_AddRef(LPOLEOBJECT lpThis)
{
	LPPSEUDOOBJ lpPseudoObj =
			((struct CPseudoObjOleObjectImpl FAR*)lpThis)->lpPseudoObj;

	OleDbgAddRefMethod(lpThis, "IOleObject");

	return PseudoObj_AddRef((LPPSEUDOOBJ)lpPseudoObj);
}


STDMETHODIMP_(ULONG) PseudoObj_OleObj_Release(LPOLEOBJECT lpThis)
{
	LPPSEUDOOBJ lpPseudoObj =
			((struct CPseudoObjOleObjectImpl FAR*)lpThis)->lpPseudoObj;

	OleDbgReleaseMethod(lpThis, "IOleObject");

	return PseudoObj_Release((LPPSEUDOOBJ)lpPseudoObj);
}


STDMETHODIMP PseudoObj_OleObj_SetClientSite(
		LPOLEOBJECT         lpThis,
		LPOLECLIENTSITE     lpClientSite
)
{
	OleDbgOut2("PseudoObj_OleObj_SetClientSite\r\n");

	// OLE2NOTE: a pseudo object does NOT support SetExtent

	return ResultFromScode(E_FAIL);
}


STDMETHODIMP PseudoObj_OleObj_GetClientSite(
		LPOLEOBJECT             lpThis,
		LPOLECLIENTSITE FAR*    lplpClientSite
)
{
	OleDbgOut2("PseudoObj_OleObj_GetClientSite\r\n");

	*lplpClientSite = NULL;

	// OLE2NOTE: a pseudo object does NOT support SetExtent

	return ResultFromScode(E_FAIL);
}



STDMETHODIMP PseudoObj_OleObj_SetHostNamesA(
		LPOLEOBJECT             lpThis,
		LPCSTR                  szContainerApp,
		LPCSTR                  szContainerObj
)
{
	OleDbgOut2("PseudoObj_OleObj_SetHostNamesA\r\n");

	// OLE2NOTE: a pseudo object does NOT support SetExtent

	return ResultFromScode(E_FAIL);
}


STDMETHODIMP PseudoObj_OleObj_SetHostNames(
		LPOLEOBJECT             lpThis,
		LPCOLESTR		szContainerApp,
		LPCOLESTR		szContainerObj
)
{
	OleDbgOut2("PseudoObj_OleObj_SetHostNames\r\n");

	// OLE2NOTE: a pseudo object does NOT support SetExtent

	return ResultFromScode(E_FAIL);
}


STDMETHODIMP PseudoObj_OleObj_Close(
		LPOLEOBJECT             lpThis,
		DWORD                   dwSaveOption
)
{
	LPPSEUDOOBJ lpPseudoObj =
			((struct CPseudoObjOleObjectImpl FAR*)lpThis)->lpPseudoObj;
	BOOL fStatus;

	OLEDBG_BEGIN2("PseudoObj_OleObj_Close\r\n")

	/* OLE2NOTE: a pseudo object's implementation of IOleObject::Close
	**    should ignore the dwSaveOption parameter. it is NOT
	**    applicable to pseudo objects.
	*/

	fStatus = PseudoObj_Close(lpPseudoObj);
	OleDbgAssertSz(fStatus == TRUE, "PseudoObj_OleObj_Close failed\r\n");

	OLEDBG_END2
	return NOERROR;
}


STDMETHODIMP PseudoObj_OleObj_SetMoniker(
		LPOLEOBJECT lpThis,
		DWORD       dwWhichMoniker,
		LPMONIKER   lpmk
)
{
	OleDbgOut2("PseudoObj_OleObj_SetMoniker\r\n");

	// OLE2NOTE: a pseudo object does NOT support SetMoniker

	return ResultFromScode(E_FAIL);
}


STDMETHODIMP PseudoObj_OleObj_GetMoniker(
		LPOLEOBJECT     lpThis,
		DWORD           dwAssign,
		DWORD           dwWhichMoniker,
		LPMONIKER FAR*  lplpmk
)
{
	LPPSEUDOOBJ lpPseudoObj =
			((struct CPseudoObjOleObjectImpl FAR*)lpThis)->lpPseudoObj;
	LPOLEDOC lpOleDoc = (LPOLEDOC)lpPseudoObj->m_lpDoc;
	LPMONIKER lpmkDoc;

	OLEDBG_BEGIN2("PseudoObj_OleObj_GetMoniker\r\n")

	lpmkDoc = OleDoc_GetFullMoniker(lpOleDoc, GETMONIKER_ONLYIFTHERE);
	*lplpmk = PseudoObj_GetFullMoniker(lpPseudoObj, lpmkDoc);

	OLEDBG_END2

	if (*lplpmk != NULL)
		return NOERROR;
	else
		return ResultFromScode(E_FAIL);
}


STDMETHODIMP PseudoObj_OleObj_InitFromData(
		LPOLEOBJECT             lpThis,
		LPDATAOBJECT            lpDataObject,
		BOOL                    fCreation,
		DWORD                   reserved
)
{
	LPPSEUDOOBJ lpPseudoObj =
			((struct CPseudoObjOleObjectImpl FAR*)lpThis)->lpPseudoObj;
	OleDbgOut2("PseudoObj_OleObj_InitFromData\r\n");

	// REVIEW: NOT YET IMPLEMENTED

	return ResultFromScode(E_NOTIMPL);
}


STDMETHODIMP PseudoObj_OleObj_GetClipboardData(
		LPOLEOBJECT             lpThis,
		DWORD                   reserved,
		LPDATAOBJECT FAR*       lplpDataObject
)
{
	LPPSEUDOOBJ lpPseudoObj =
			((struct CPseudoObjOleObjectImpl FAR*)lpThis)->lpPseudoObj;
	OleDbgOut2("PseudoObj_OleObj_GetClipboardData\r\n");

	// REVIEW: NOT YET IMPLEMENTED

	return ResultFromScode(E_NOTIMPL);
}


STDMETHODIMP PseudoObj_OleObj_DoVerb(
		LPOLEOBJECT             lpThis,
		LONG                    lVerb,
		LPMSG                   lpmsg,
		LPOLECLIENTSITE         lpActiveSite,
		LONG                    lindex,
		HWND                    hwndParent,
		LPCRECT                 lprcPosRect
)
{
	LPPSEUDOOBJ lpPseudoObj =
			((struct CPseudoObjOleObjectImpl FAR*)lpThis)->lpPseudoObj;
	LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpPseudoObj->m_lpDoc;
	LPSERVERDOC lpServerDoc = lpPseudoObj->m_lpDoc;
	LINERANGE lrSel;
	HRESULT hrErr;

	OLEDBG_BEGIN2("PseudoObj_OleObj_DoVerb\r\n");

	/* OLE2NOTE: we must first ask our Document to perform the same
	**    verb. then if the verb is NOT OLEIVERB_HIDE we should also
	**    select the range of our pseudo object.
	**    however, we must give our document its own embedding site as
	**    its active site.
	*/
	hrErr = SvrDoc_OleObj_DoVerb(
			(LPOLEOBJECT)&lpServerDoc->m_OleObject,
			lVerb,
			lpmsg,
			lpServerDoc->m_lpOleClientSite,
			lindex,
			NULL,   /* we have no hwndParent to give */
			NULL    /* we have no lprcPosRect to give */
	);
	if (FAILED(hrErr)) {
		OLEDBG_END2
		return hrErr;
	}

	if (lVerb != OLEIVERB_HIDE) {
		PseudoObj_GetSel(lpPseudoObj, &lrSel);
		OutlineDoc_SetSel(lpOutlineDoc, &lrSel);
	}

	OLEDBG_END2
	return NOERROR;
}



STDMETHODIMP PseudoObj_OleObj_EnumVerbs(
		LPOLEOBJECT         lpThis,
		LPENUMOLEVERB FAR*  lplpenumOleVerb
)
{
	OleDbgOut2("PseudoObj_OleObj_EnumVerbs\r\n");

	/* OLE2NOTE: we must make sure to set all out parameters to NULL. */
	*lplpenumOleVerb = NULL;

	/* A pseudo object may NOT return OLE_S_USEREG; they must call the
	**    OleReg* API or provide their own implementation. Because this
	**    pseudo object does NOT implement IPersist, simply a low-level
	**    remoting handler (ProxyManager) object as opposed to a
	**    DefHandler object is used as the handler for the pseudo
	**    object in a clients process space. The ProxyManager does NOT
	**    handle the OLE_S_USEREG return values.
	*/
	return OleRegEnumVerbs((REFCLSID)&CLSID_APP, lplpenumOleVerb);
}


STDMETHODIMP PseudoObj_OleObj_Update(LPOLEOBJECT lpThis)
{
	OleDbgOut2("PseudoObj_OleObj_Update\r\n");

	/* OLE2NOTE: a server-only app is always "up-to-date".
	**    a container-app which contains links where the link source
	**    has changed since the last update of the link would be
	**    considered "out-of-date". the "Update" method instructs the
	**    object to get an update from any out-of-date links.
	*/

	return NOERROR;
}


STDMETHODIMP PseudoObj_OleObj_IsUpToDate(LPOLEOBJECT lpThis)
{
	LPPSEUDOOBJ lpPseudoObj =
			((struct CPseudoObjOleObjectImpl FAR*)lpThis)->lpPseudoObj;
	OleDbgOut2("PseudoObj_OleObj_IsUpToDate\r\n");

	/* OLE2NOTE: a server-only app is always "up-to-date".
	**    a container-app which contains links where the link source
	**    has changed since the last update of the link would be
	**    considered "out-of-date".
	*/
	return NOERROR;
}


STDMETHODIMP PseudoObj_OleObj_GetUserClassID(
		LPOLEOBJECT             lpThis,
		LPCLSID                 lpclsid
)
{
	LPPSEUDOOBJ lpPseudoObj =
			((struct CPseudoObjOleObjectImpl FAR*)lpThis)->lpPseudoObj;
	LPSERVERDOC lpServerDoc = (LPSERVERDOC)lpPseudoObj->m_lpDoc;
	OleDbgOut2("PseudoObj_OleObj_GetUserClassID\r\n");

	/* OLE2NOTE: 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, lpclsid);
}


STDMETHODIMP PseudoObj_OleObj_GetUserTypeA(
		LPOLEOBJECT             lpThis,
		DWORD                   dwFormOfType,
		LPSTR FAR*              lpszUserType
)
{
	LPPSEUDOOBJ lpPseudoObj =
			((struct CPseudoObjOleObjectImpl FAR*)lpThis)->lpPseudoObj;
	LPSERVERDOC lpServerDoc = (LPSERVERDOC)lpPseudoObj->m_lpDoc;
	OleDbgOut2("PseudoObj_OleObj_GetUserType\r\n");

	/* OLE2NOTE: we must make sure to set all out parameters to NULL. */
	*lpszUserType = NULL;

	/* OLE2NOTE: we must be carefull to return the correct user type here.
	**    if we are currently preforming a "TreatAs (aka. ActivateAs)"
	**    operation then we need to return the user type name that
	**    corresponds to the class of the object we are currently
	**    emmulating. otherwise we should return our normal user type
	**    name corresponding to our own class. This routine determines
	**    the current clsid in effect.
	**
	**    A pseudo object may NOT return OLE_S_USEREG; they must call the
	**    OleReg* API or provide their own implementation. Because this
	**    pseudo object does NOT implement IPersist, simply a low-level
	**    remoting handler (ProxyManager) object as opposed to a
	**    DefHandler object is used as the handler for the pseudo
	**    object in a clients process space. The ProxyManager does NOT
	**    handle the OLE_S_USEREG return values.
	*/
#if defined( SVR_TREATAS )
	if (! IsEqualCLSID(&lpServerDoc->m_clsidTreatAs, &CLSID_NULL) )
		return OleRegGetUserTypeA(
			&lpServerDoc->m_clsidTreatAs,dwFormOfType,lpszUserType);
	else
#endif  // SVR_TREATAS

	return OleRegGetUserTypeA(&CLSID_APP, dwFormOfType, lpszUserType);
}

STDMETHODIMP PseudoObj_OleObj_GetUserType(
		LPOLEOBJECT             lpThis,
		DWORD                   dwFormOfType,
		LPOLESTR FAR*		lpszUserType
)
{
    LPSTR pstr;

    HRESULT hr = PseudoObj_OleObj_GetUserTypeA(lpThis, dwFormOfType, &pstr);

    CopyAndFreeSTR(pstr, lpszUserType);

    return hr;
}



STDMETHODIMP PseudoObj_OleObj_SetExtent(
		LPOLEOBJECT             lpThis,
		DWORD                   dwDrawAspect,
		LPSIZEL                 lplgrc
)
{
	OleDbgOut2("PseudoObj_OleObj_SetExtent\r\n");

	// OLE2NOTE: a pseudo object does NOT support SetExtent

	return ResultFromScode(E_FAIL);
}


STDMETHODIMP PseudoObj_OleObj_GetExtent(
		LPOLEOBJECT             lpThis,
		DWORD                   dwDrawAspect,
		LPSIZEL                 lpsizel
)
{
	LPPSEUDOOBJ lpPseudoObj =
			((struct CPseudoObjOleObjectImpl FAR*)lpThis)->lpPseudoObj;
	OleDbgOut2("PseudoObj_OleObj_GetExtent\r\n");

	/* OLE2NOTE: it is VERY important to check which aspect the caller
	**    is asking about. an object implemented by a server EXE MAY
	**    fail to return extents when asked for DVASPECT_ICON.
	*/
	if (dwDrawAspect == DVASPECT_CONTENT) {
		PseudoObj_GetExtent(lpPseudoObj, lpsizel);
		return NOERROR;
	}
	else
	{
		return ResultFromScode(E_FAIL);
	}
}


STDMETHODIMP PseudoObj_OleObj_Advise(
		LPOLEOBJECT lpThis,
		LPADVISESINK lpAdvSink,
		LPDWORD lpdwConnection
)
{
	LPPSEUDOOBJ lpPseudoObj =
			((struct CPseudoObjOleObjectImpl FAR*)lpThis)->lpPseudoObj;
	HRESULT hrErr;
	SCODE   sc;
	OLEDBG_BEGIN2("PseudoObj_OleObj_Advise\r\n");

	if (lpPseudoObj->m_lpOleAdviseHldr == NULL &&
		CreateOleAdviseHolder(&lpPseudoObj->m_lpOleAdviseHldr) != NOERROR) {
		sc = E_OUTOFMEMORY;
		goto error;
	}

	OLEDBG_BEGIN2("IOleAdviseHolder::Advise called\r\n")
	hrErr = lpPseudoObj->m_lpOleAdviseHldr->lpVtbl->Advise(
			lpPseudoObj->m_lpOleAdviseHldr,
			lpAdvSink,
			lpdwConnection
	);
	OLEDBG_END2

	OLEDBG_END2
	return hrErr;

error:
	OLEDBG_END2
	return ResultFromScode(sc);
}


STDMETHODIMP PseudoObj_OleObj_Unadvise(LPOLEOBJECT lpThis, DWORD dwConnection)
{
	LPPSEUDOOBJ lpPseudoObj =
			((struct CPseudoObjOleObjectImpl FAR*)lpThis)->lpPseudoObj;
	HRESULT hrErr;
	SCODE   sc;

	OLEDBG_BEGIN2("PseudoObj_OleObj_Unadvise\r\n");

	if (lpPseudoObj->m_lpOleAdviseHldr == NULL) {
		sc = E_FAIL;
		goto error;
	}

	OLEDBG_BEGIN2("IOleAdviseHolder::Unadvise called\r\n")
	hrErr = lpPseudoObj->m_lpOleAdviseHldr->lpVtbl->Unadvise(
			lpPseudoObj->m_lpOleAdviseHldr,
			dwConnection
	);
	OLEDBG_END2

	OLEDBG_END2
	return hrErr;

error:
	OLEDBG_END2
	return ResultFromScode(sc);
}


STDMETHODIMP PseudoObj_OleObj_EnumAdvise(
		LPOLEOBJECT lpThis,
		LPENUMSTATDATA FAR* lplpenumAdvise
)
{
	LPPSEUDOOBJ lpPseudoObj =
			((struct CPseudoObjOleObjectImpl FAR*)lpThis)->lpPseudoObj;
	HRESULT hrErr;
	SCODE   sc;

	OLEDBG_BEGIN2("PseudoObj_OleObj_EnumAdvise\r\n");

	/* OLE2NOTE: we must make sure to set all out parameters to NULL. */
	*lplpenumAdvise = NULL;

	if (lpPseudoObj->m_lpOleAdviseHldr == NULL) {
		sc = E_FAIL;
		goto error;
	}

	OLEDBG_BEGIN2("IOleAdviseHolder::EnumAdvise called\r\n")
	hrErr = lpPseudoObj->m_lpOleAdviseHldr->lpVtbl->EnumAdvise(
			lpPseudoObj->m_lpOleAdviseHldr,
			lplpenumAdvise
	);
	OLEDBG_END2

	OLEDBG_END2
	return hrErr;

error:
	OLEDBG_END2
	return ResultFromScode(sc);
}


STDMETHODIMP PseudoObj_OleObj_GetMiscStatus(
		LPOLEOBJECT             lpThis,
		DWORD                   dwAspect,
		DWORD FAR*              lpdwStatus
)
{
	LPPSEUDOOBJ lpPseudoObj =
			((struct CPseudoObjOleObjectImpl FAR*)lpThis)->lpPseudoObj;
	LPOUTLINEDOC  lpOutlineDoc = (LPOUTLINEDOC)lpPseudoObj->m_lpDoc;
	OleDbgOut2("PseudoObj_OleObj_GetMiscStatus\r\n");

	/* Get our default MiscStatus for the given Aspect. this
	**    information is registered in the RegDB. We query the RegDB
	**    here to guarantee that the value returned from this method
	**    agrees with the values in RegDB. in this way we only have to
	**    maintain the info in one place (in the RegDB). Alternatively
	**    we could have the values hard coded here.
	**
	** OLE2NOTE: A pseudo object may NOT return OLE_S_USEREG; they must
	**    call the
	**    OleReg* API or provide their own implementation. Because this
	**    pseudo object does NOT implement IPersist, simply a low-level
	**    remoting handler (ProxyManager) object as opposed to a
	**    DefHandler object is used as the handler for the pseudo
	**    object in a clients process space. The ProxyManager does NOT
	**    handle the OLE_S_USEREG return values.
	*/
	OleRegGetMiscStatus((REFCLSID)&CLSID_APP, dwAspect, lpdwStatus);

	/* OLE2NOTE: check if the pseudo object is compatible to be
	**    linked by an OLE 1.0 container. it is compatible if
	**    either the pseudo object is an untitled document or a
	**    file-based document. if the pseudo object is part of
	**    an embedded object, then it is NOT compatible to be
	**    linked by an OLE 1.0 container. if it is compatible then
	**    we should include OLEMISC_CANLINKBYOLE1 as part of the
	**    dwStatus flags.
	*/
	if (lpOutlineDoc->m_docInitType == DOCTYPE_NEW ||
		lpOutlineDoc->m_docInitType == DOCTYPE_FROMFILE)
		*lpdwStatus |= OLEMISC_CANLINKBYOLE1;

	return NOERROR;
}


STDMETHODIMP PseudoObj_OleObj_SetColorScheme(
		LPOLEOBJECT             lpThis,
		LPLOGPALETTE            lpLogpal
)
{
	OleDbgOut2("PseudoObj_OleObj_SetColorScheme\r\n");

	// REVIEW: NOT YET IMPLEMENTED

	return ResultFromScode(E_NOTIMPL);
}


/*************************************************************************
** PseudoObj::IDataObject interface implementation
*************************************************************************/

STDMETHODIMP PseudoObj_DataObj_QueryInterface (
		LPDATAOBJECT      lpThis,
		REFIID            riid,
		LPVOID FAR*       lplpvObj
)
{
	LPPSEUDOOBJ lpPseudoObj =
			((struct CPseudoObjDataObjectImpl FAR*)lpThis)->lpPseudoObj;

	return PseudoObj_QueryInterface(lpPseudoObj, riid, lplpvObj);
}


STDMETHODIMP_(ULONG) PseudoObj_DataObj_AddRef(LPDATAOBJECT lpThis)
{
	LPPSEUDOOBJ lpPseudoObj =
			((struct CPseudoObjDataObjectImpl FAR*)lpThis)->lpPseudoObj;

	OleDbgAddRefMethod(lpThis, "IDataObject");

	return PseudoObj_AddRef((LPPSEUDOOBJ)lpPseudoObj);
}


STDMETHODIMP_(ULONG) PseudoObj_DataObj_Release (LPDATAOBJECT lpThis)
{
	LPPSEUDOOBJ lpPseudoObj =
			((struct CPseudoObjDataObjectImpl FAR*)lpThis)->lpPseudoObj;

	OleDbgReleaseMethod(lpThis, "IDataObject");

	return PseudoObj_Release((LPPSEUDOOBJ)lpPseudoObj);
}


STDMETHODIMP PseudoObj_DataObj_GetData (
		LPDATAOBJECT    lpThis,
		LPFORMATETC     lpformatetc,
		LPSTGMEDIUM     lpMedium
)
{
	LPPSEUDOOBJ lpPseudoObj =
			((struct CPseudoObjDataObjectImpl FAR*)lpThis)->lpPseudoObj;
	LPSERVERDOC  lpServerDoc = lpPseudoObj->m_lpDoc;
	LPOUTLINEDOC  lpOutlineDoc = (LPOUTLINEDOC)lpServerDoc;
	LPSERVERAPP lpServerApp = (LPSERVERAPP)g_lpApp;
	LPOLEAPP  lpOleApp = (LPOLEAPP)lpServerApp;
	LPOUTLINEAPP  lpOutlineApp = (LPOUTLINEAPP)lpServerApp;
	LINERANGE lrSel;
	SCODE sc = S_OK;
	OLEDBG_BEGIN2("PseudoObj_DataObj_GetData\r\n")

	PseudoObj_GetSel(lpPseudoObj, &lrSel);

	/* OLE2NOTE: we must make sure to set all out parameters to NULL. */
	lpMedium->tymed = TYMED_NULL;
	lpMedium->pUnkForRelease = NULL;    // we transfer ownership to caller
	lpMedium->hGlobal = NULL;

	if (lpformatetc->cfFormat == lpOutlineApp->m_cfOutline) {
		// Verify caller asked for correct medium
		if (!(lpformatetc->tymed & TYMED_HGLOBAL)) {
			sc = DATA_E_FORMATETC;
			goto error;
		}

		lpMedium->hGlobal = OutlineDoc_GetOutlineData (lpOutlineDoc,&lrSel);
		if (! lpMedium->hGlobal) return ResultFromScode(E_OUTOFMEMORY);
		lpMedium->tymed = TYMED_HGLOBAL;
		OleDbgOut3("PseudoObj_DataObj_GetData: rendered CF_OUTLINE\r\n");

	} else if(lpformatetc->cfFormat == CF_METAFILEPICT &&
		(lpformatetc->dwAspect & DVASPECT_CONTENT) ) {
		// Verify caller asked for correct medium
		if (!(lpformatetc->tymed & TYMED_MFPICT)) {
			sc = DATA_E_FORMATETC;
			goto error;
		}

		lpMedium->hGlobal=ServerDoc_GetMetafilePictData(lpServerDoc,&lrSel);
		if (! lpMedium->hGlobal) {
			sc = E_OUTOFMEMORY;
			goto error;
		}
		lpMedium->tymed = TYMED_MFPICT;
		OleDbgOut3("PseudoObj_DataObj_GetData: rendered CF_METAFILEPICT\r\n");

	} else if (lpformatetc->cfFormat == CF_METAFILEPICT &&
		(lpformatetc->dwAspect & DVASPECT_ICON) ) {
		CLSID clsid;
		// Verify caller asked for correct medium
		if (!(lpformatetc->tymed & TYMED_MFPICT)) {
			sc = DATA_E_FORMATETC;
			goto error;
		}

		/* OLE2NOTE: we should return the default icon for our class.
		**    we must be carefull to use the correct CLSID here.
		**    if we are currently preforming a "TreatAs (aka. ActivateAs)"
		**    operation then we need to use the class of the object
		**    written in the storage of the object. otherwise we would
		**    use our own class id.
		*/
		if (ServerDoc_GetClassID(lpServerDoc, (LPCLSID)&clsid) != NOERROR) {
			sc = DATA_E_FORMATETC;
			goto error;
		}

		lpMedium->hGlobal=GetIconOfClass(
				g_lpApp->m_hInst,(REFCLSID)&clsid, NULL, FALSE);
		if (! lpMedium->hGlobal) {
			sc = E_OUTOFMEMORY;
			goto error;
		}

		lpMedium->tymed = TYMED_MFPICT;
		OleDbgOut3("PseudoObj_DataObj_GetData: rendered CF_METAFILEPICT (icon)\r\n");
		return NOERROR;

	} else if (lpformatetc->cfFormat == CF_TEXT) {
		// Verify caller asked for correct medium
		if (!(lpformatetc->tymed & TYMED_HGLOBAL)) {
			sc = DATA_E_FORMATETC;
			goto error;
		}

		lpMedium->hGlobal = OutlineDoc_GetTextData (lpOutlineDoc, &lrSel);
		if (! lpMedium->hGlobal) {
			sc = E_OUTOFMEMORY;
			goto error;
		}
		lpMedium->tymed = TYMED_HGLOBAL;
		OleDbgOut3("PseudoObj_DataObj_GetData: rendered CF_TEXT\r\n");

	} else {
		sc = DATA_E_FORMATETC;
		goto error;
	}

	OLEDBG_END2
	return NOERROR;

error:
	OLEDBG_END2
	return ResultFromScode(sc);
}


STDMETHODIMP PseudoObj_DataObj_GetDataHere (
		LPDATAOBJECT    lpThis,
		LPFORMATETC     lpformatetc,
		LPSTGMEDIUM     lpMedium
)
{
	LPPSEUDOOBJ lpPseudoObj =
			((struct CPseudoObjDataObjectImpl FAR*)lpThis)->lpPseudoObj;
	LPSERVERDOC  lpServerDoc = lpPseudoObj->m_lpDoc;
	LPOUTLINEDOC  lpOutlineDoc = (LPOUTLINEDOC)lpServerDoc;
	LPSERVERAPP lpServerApp = (LPSERVERAPP)g_lpApp;
	LPOLEAPP  lpOleApp = (LPOLEAPP)lpServerApp;
	LPOUTLINEAPP  lpOutlineApp = (LPOUTLINEAPP)lpServerApp;
	OleDbgOut("PseudoObj_DataObj_GetDataHere\r\n");

	/* Caller is requesting data to be returned in Caller allocated
	**    medium, but we do NOT support this. we only support
	**    global memory blocks that WE allocate for the caller.
	*/
	return ResultFromScode(DATA_E_FORMATETC);
}


STDMETHODIMP PseudoObj_DataObj_QueryGetData (
		LPDATAOBJECT    lpThis,
		LPFORMATETC     lpformatetc
)
{
	LPPSEUDOOBJ lpPseudoObj =
			((struct CPseudoObjDataObjectImpl FAR*)lpThis)->lpPseudoObj;
	LPSERVERDOC  lpServerDoc = lpPseudoObj->m_lpDoc;
	LPOUTLINEDOC  lpOutlineDoc = (LPOUTLINEDOC)lpServerDoc;
	LPSERVERAPP lpServerApp = (LPSERVERAPP)g_lpApp;
	LPOLEAPP  lpOleApp = (LPOLEAPP)lpServerApp;
	LPOUTLINEAPP  lpOutlineApp = (LPOUTLINEAPP)lpServerApp;
	OleDbgOut2("PseudoObj_DataObj_QueryGetData\r\n");

	/* Caller is querying if we support certain format but does not
	**    want any data actually returned.
	*/
	if (lpformatetc->cfFormat == CF_METAFILEPICT &&
		(lpformatetc->dwAspect & (DVASPECT_CONTENT | DVASPECT_ICON)) ) {
		return OleStdQueryFormatMedium(lpformatetc, TYMED_MFPICT);

	} else if (lpformatetc->cfFormat == (lpOutlineApp)->m_cfOutline ||
			lpformatetc->cfFormat == CF_TEXT) {
		return OleStdQueryFormatMedium(lpformatetc, TYMED_HGLOBAL);
	}

	return ResultFromScode(DATA_E_FORMATETC);
}


STDMETHODIMP PseudoObj_DataObj_GetCanonicalFormatEtc(
		LPDATAOBJECT    lpThis,
		LPFORMATETC     lpformatetc,
		LPFORMATETC     lpformatetcOut
)
{
	HRESULT hrErr;
	OleDbgOut2("PseudoObj_DataObj_GetCanonicalFormatEtc\r\n");

	if (!lpformatetcOut)
		return ResultFromScode(E_INVALIDARG);

	/* OLE2NOTE: we must make sure to set all out parameters to NULL. */
	lpformatetcOut->ptd = NULL;

	if (!lpformatetc)
		return ResultFromScode(E_INVALIDARG);

	// OLE2NOTE: we must validate that the format requested is supported
	if ((hrErr=lpThis->lpVtbl->QueryGetData(lpThis,lpformatetc)) != NOERROR)
		return hrErr;

	/* OLE2NOTE: an app that is insensitive to target device (as the
	**    Outline Sample is) should fill in the lpformatOut parameter
	**    but NULL out the "ptd" field; it should return NOERROR if the
	**    input formatetc->ptd what non-NULL. this tells the caller
	**    that it is NOT necessary to maintain a separate screen
	**    rendering and printer rendering. if should return
	**    DATA_S_SAMEFORMATETC if the input and output formatetc's are
	**    identical.
	*/

	*lpformatetcOut = *lpformatetc;
	if (lpformatetc->ptd == NULL)
		return ResultFromScode(DATA_S_SAMEFORMATETC);
	else {
		lpformatetcOut->ptd = NULL;
		return NOERROR;
	}
}


STDMETHODIMP PseudoObj_DataObj_SetData (
		LPDATAOBJECT    lpThis,
		LPFORMATETC     lpformatetc,
		LPSTGMEDIUM     lpmedium,
		BOOL            fRelease
)
{
	LPPSEUDOOBJ lpPseudoObj =
			((struct CPseudoObjDataObjectImpl FAR*)lpThis)->lpPseudoObj;
	LPSERVERDOC  lpServerDoc = lpPseudoObj->m_lpDoc;
	LPOUTLINEDOC  lpOutlineDoc = (LPOUTLINEDOC)lpServerDoc;
	LPSERVERAPP lpServerApp = (LPSERVERAPP)g_lpApp;

	OleDbgOut2("PseudoObj_DataObj_SetData\r\n");

	// REVIEW: NOT-YET-IMPLEMENTED
	return ResultFromScode(E_NOTIMPL);
}


STDMETHODIMP PseudoObj_DataObj_EnumFormatEtc(
		LPDATAOBJECT            lpThis,
		DWORD                   dwDirection,
		LPENUMFORMATETC FAR*    lplpenumFormatEtc
)
{
	SCODE sc;
	OleDbgOut2("PseudoObj_DataObj_EnumFormatEtc\r\n");

	/* OLE2NOTE: a pseudo object only needs to enumerate the static list
	**    of formats that are registered for our app in the
	**    registration database. it is NOT
	**    required that a pseudo object (ie. non-DataTransferDoc)
	**    enumerate the OLE formats: CF_LINKSOURCE, CF_EMBEDSOURCE, or
	**    CF_EMBEDDEDOBJECT. we do NOT use pseudo objects for data
	**    transfers.
	**
	**    A pseudo object may NOT return OLE_S_USEREG; they must call the
	**    OleReg* API or provide their own implementation. Because this
	**    pseudo object does NOT implement IPersist, simply a low-level
	**    remoting handler (ProxyManager) object as opposed to a
	**    DefHandler object is used as the handler for the pseudo
	**    object in a clients process space. The ProxyManager does NOT
	**    handle the OLE_S_USEREG return values.
	*/
	if (dwDirection == DATADIR_GET)
		return OleRegEnumFormatEtc(
				(REFCLSID)&CLSID_APP, dwDirection, lplpenumFormatEtc);
	else if (dwDirection == DATADIR_SET)
		sc = E_NOTIMPL;
	else
		sc = E_INVALIDARG;

	return ResultFromScode(sc);
}


STDMETHODIMP PseudoObj_DataObj_DAdvise(
		LPDATAOBJECT    lpThis,
		FORMATETC FAR*  lpFormatetc,
		DWORD           advf,
		LPADVISESINK    lpAdvSink,
		DWORD FAR*      lpdwConnection
)
{
	LPPSEUDOOBJ lpPseudoObj =
			((struct CPseudoObjDataObjectImpl FAR*)lpThis)->lpPseudoObj;
	HRESULT hrErr;
	SCODE   sc;

	OLEDBG_BEGIN2("PseudoObj_DataObj_DAdvise\r\n")

	/* OLE2NOTE: we must make sure to set all out parameters to NULL. */
	*lpdwConnection = 0;

	/* OLE2NOTE: we should validate if the caller is setting up an
	**    Advise for a data type that we support. we must
	**    explicitly allow an advise for the "wildcard" advise.
	*/
	if ( !( lpFormatetc->cfFormat == 0 &&
		lpFormatetc->ptd == NULL &&
		lpFormatetc->dwAspect == -1L &&
		lpFormatetc->lindex == -1L &&
		lpFormatetc->tymed == -1L) &&
		(hrErr = PseudoObj_DataObj_QueryGetData(lpThis, lpFormatetc))
			!= NOERROR) {
		sc = GetScode(hrErr);
		goto error;
	}

	if (lpPseudoObj->m_lpDataAdviseHldr == NULL &&
		CreateDataAdviseHolder(&lpPseudoObj->m_lpDataAdviseHldr) != NOERROR) {
		sc = E_OUTOFMEMORY;
		goto error;
	}

	OLEDBG_BEGIN2("IOleAdviseHolder::Advise called\r\n")
	hrErr = lpPseudoObj->m_lpDataAdviseHldr->lpVtbl->Advise(
			lpPseudoObj->m_lpDataAdviseHldr,
			(LPDATAOBJECT)&lpPseudoObj->m_DataObject,
			lpFormatetc,
			advf,
			lpAdvSink,
			lpdwConnection
	);
	OLEDBG_END2

	OLEDBG_END2
	return hrErr;

error:
	OLEDBG_END2
	return ResultFromScode(sc);
}


STDMETHODIMP PseudoObj_DataObj_DUnadvise(LPDATAOBJECT lpThis, DWORD dwConnection)
{
	LPPSEUDOOBJ lpPseudoObj =
			((struct CPseudoObjDataObjectImpl FAR*)lpThis)->lpPseudoObj;
	HRESULT hrErr;
	SCODE   sc;

	OLEDBG_BEGIN2("PseudoObj_DataObj_Unadvise\r\n");

	// no one registered
	if (lpPseudoObj->m_lpDataAdviseHldr == NULL) {
		sc = E_FAIL;
		goto error;
	}

	OLEDBG_BEGIN2("IOleAdviseHolder::DUnadvise called\r\n")
	hrErr = lpPseudoObj->m_lpDataAdviseHldr->lpVtbl->Unadvise(
			lpPseudoObj->m_lpDataAdviseHldr,
			dwConnection
	);
	OLEDBG_END2

	OLEDBG_END2
	return hrErr;

error:
	OLEDBG_END2
	return ResultFromScode(sc);
}


STDMETHODIMP PseudoObj_DataObj_EnumAdvise(
		LPDATAOBJECT lpThis,
		LPENUMSTATDATA FAR* lplpenumAdvise
)
{
	LPPSEUDOOBJ lpPseudoObj =
			((struct CPseudoObjDataObjectImpl FAR*)lpThis)->lpPseudoObj;
	HRESULT hrErr;
	SCODE   sc;

	OLEDBG_BEGIN2("PseudoObj_DataObj_EnumAdvise\r\n");

	/* OLE2NOTE: we must make sure to set all out parameters to NULL. */
	*lplpenumAdvise = NULL;

	if (lpPseudoObj->m_lpDataAdviseHldr == NULL) {
		sc = E_FAIL;
		goto error;
	}

	OLEDBG_BEGIN2("IOleAdviseHolder::EnumAdvise called\r\n")
	hrErr = lpPseudoObj->m_lpDataAdviseHldr->lpVtbl->EnumAdvise(
			lpPseudoObj->m_lpDataAdviseHldr,
			lplpenumAdvise
	);
	OLEDBG_END2

	OLEDBG_END2
	return hrErr;

error:
	OLEDBG_END2
	return ResultFromScode(sc);
}