/*************************************************************************
**
**    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,
            OLESTR("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,
            OLESTR("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,
            OLESTR("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,
            OLESTR("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;

   // artificial AddRef in case object is destroyed during call
   PseudoObj_AddRef(lpPseudoObj);

   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;
   }
   // release artificial AddRef
   PseudoObj_Release(lpPseudoObj);
}


/* 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) {
      CreateItemMoniker(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_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;

   // artificial AddRef in case object is destroyed during call
   PseudoObj_OleObj_AddRef(lpThis);

   OLEDBG_BEGIN2("PseudoObj_OleObj_GetMoniker\r\n")

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

   OLEDBG_END2

   if (*lplpmk != NULL)
   {
      // release artificial AddRef
      PseudoObj_OleObj_Release(lpThis);
      return NOERROR;
   }
   else
   {
      // release artificial AddRef
      PseudoObj_OleObj_Release(lpThis);
      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;

   // artificial AddRef in case object is destroyed during call
   PseudoObj_OleObj_AddRef(lpThis);

   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
      // release artificial AddRef
      PseudoObj_OleObj_Release(lpThis);
      return hrErr;
   }

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

   OLEDBG_END2
   // release artificial AddRef
   PseudoObj_OleObj_Release(lpThis);
   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_GetUserType(
      LPOLEOBJECT             lpThis,
      DWORD                   dwFormOfType,
      LPOLESTR 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 OleRegGetUserType(
         (REFCLSID)&lpServerDoc->m_clsidTreatAs,dwFormOfType,lpszUserType);
   else
#endif  // SVR_TREATAS

   return OleRegGetUserType((REFCLSID)&CLSID_APP,dwFormOfType,lpszUserType);
}


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;

   // artificial AddRef in case object is destroyed during call
   PseudoObj_OleObj_AddRef(lpThis);

   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
   // release artificial AddRef
   PseudoObj_OleObj_Release(lpThis);
   return hrErr;

error:
   OLEDBG_END2
   // release artificial AddRef
   PseudoObj_OleObj_Release(lpThis);
   return ResultFromScode(sc);
}


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

   // artificial AddRef in case object is destroyed during call
   PseudoObj_OleObj_AddRef(lpThis);

   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
   // release artificial AddRef
   PseudoObj_OleObj_Release(lpThis);
   return hrErr;

error:
   OLEDBG_END2
   // release artificial AddRef
   PseudoObj_OleObj_Release(lpThis);
   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;

   // artificial AddRef in case object is destroyed during call
   PseudoObj_DataObj_AddRef(lpThis);

   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->u.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->u.hGlobal = OutlineDoc_GetOutlineData (lpOutlineDoc,&lrSel);
      if (! lpMedium->u.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->u.hGlobal=ServerDoc_GetMetafilePictData(lpServerDoc,&lrSel);
      if (! lpMedium->u.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->u.hGlobal=GetIconOfClass(
            g_lpApp->m_hInst,(REFCLSID)&clsid, NULL, FALSE);
      if (! lpMedium->u.hGlobal) {
         sc = E_OUTOFMEMORY;
         goto error;
      }

      lpMedium->tymed = TYMED_MFPICT;
      OleDbgOut3("PseudoObj_DataObj_GetData: rendered CF_METAFILEPICT (icon)\r\n");
      // release artificial AddRef
      PseudoObj_DataObj_Release(lpThis);
      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->u.hGlobal = OutlineDoc_GetTextData (lpOutlineDoc, &lrSel);
      if (! lpMedium->u.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
   // release artificial AddRef
   PseudoObj_DataObj_Release(lpThis);
   return NOERROR;

error:
   OLEDBG_END2
   // release artificial AddRef
   PseudoObj_DataObj_Release(lpThis);
   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;

   // artificial AddRef in case object is destroyed during call
   PseudoObj_DataObj_AddRef(lpThis);

   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)) ) {
      // release artificial AddRef
      PseudoObj_DataObj_Release(lpThis);
      return OleStdQueryFormatMedium(lpformatetc, TYMED_MFPICT);

   } else if (lpformatetc->cfFormat == (lpOutlineApp)->m_cfOutline ||
         lpformatetc->cfFormat == CF_TEXT) {
      // release artificial AddRef
      PseudoObj_DataObj_Release(lpThis);
      return OleStdQueryFormatMedium(lpformatetc, TYMED_HGLOBAL);
   }

   // release artificial AddRef
   PseudoObj_DataObj_Release(lpThis);
   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;

   // artificial AddRef in case object is destroyed during call
   PseudoObj_DataObj_AddRef(lpThis);

   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.
   */
#ifdef WIN32
   if ( !( lpFormatetc->cfFormat == 0 &&
      lpFormatetc->ptd == 0 &&
#else
   if ( !( lpFormatetc->cfFormat == NULL &&
      lpFormatetc->ptd == NULL &&
#endif
      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
   // release artificial AddRef
   PseudoObj_DataObj_Release(lpThis);
   return hrErr;

error:
   OLEDBG_END2
   // release artificial AddRef
   PseudoObj_DataObj_Release(lpThis);
   return ResultFromScode(sc);
}


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

   // artificial AddRef in case object is destroyed during call
   PseudoObj_DataObj_AddRef(lpThis);
   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
   // release artificial AddRef
   PseudoObj_DataObj_Release(lpThis);
   return hrErr;

error:
   OLEDBG_END2
   // release artificial AddRef
   PseudoObj_DataObj_Release(lpThis);
   return ResultFromScode(sc);
}


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

   // artificial AddRef in case object is destroyed during call
   PseudoObj_DataObj_AddRef(lpThis);

   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
   // release artificial AddRef
   PseudoObj_DataObj_Release(lpThis);
   return hrErr;

error:
   OLEDBG_END2
   // release artificial AddRef
   PseudoObj_DataObj_Release(lpThis);
   return ResultFromScode(sc);
}
