home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / com / inole2 / chap17 / patron / tenant.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1995-05-03  |  48.6 KB  |  2,011 lines

  1. /*
  2.  * TENANT.CPP
  3.  * Patron Chapter 17
  4.  *
  5.  * Implementation of the CTentant class which holds information
  6.  * for a single object on a page.  It maintains position, references
  7.  * to data, and a storage.
  8.  *
  9.  * Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
  10.  *
  11.  * Kraig Brockschmidt, Microsoft
  12.  * Internet  :  kraigb@microsoft.com
  13.  * Compuserve:  >INTERNET:kraigb@microsoft.com
  14.  */
  15.  
  16.  
  17. #include "patron.h"
  18.  
  19.  
  20. /*
  21.  * CTenant::CTenant
  22.  * CTenant::~CTenant
  23.  *
  24.  * Constructor Parameters:
  25.  *  dwID            DWORD identifier for this page.
  26.  *  hWnd            HWND of the pages window.
  27.  *  pPG             PCPages to the parent structure.
  28.  */
  29.  
  30. CTenant::CTenant(DWORD dwID, HWND hWnd, PCPages pPG)
  31.     {
  32.     m_hWnd=hWnd;
  33.     m_dwID=dwID;
  34.  
  35.     m_fInitialized=0;
  36.     m_pIStorage=NULL;
  37.     m_cOpens=0;
  38.  
  39.     m_pObj=NULL;
  40.     m_pPG =pPG;
  41.     m_clsID=CLSID_NULL;
  42.     m_fSetExtent=FALSE;
  43.  
  44.     //CHAPTER17MOD
  45.     m_cRef=0;
  46.     m_pIOleObject=NULL;
  47.     m_pIViewObject2=NULL;
  48.     m_grfMisc=0;
  49.  
  50.     m_pImpIOleClientSite=NULL;
  51.     m_pImpIAdviseSink=NULL;
  52.  
  53.     m_fRepaintEnabled=TRUE;
  54.     //End CHAPTER17MOD
  55.     return;
  56.     }
  57.  
  58.  
  59. CTenant::~CTenant(void)
  60.     {
  61.     //CHAPTER17MOD
  62.     //Object pointers cleaned up in Close.
  63.  
  64.     DeleteInterfaceImp(m_pImpIAdviseSink);
  65.     DeleteInterfaceImp(m_pImpIOleClientSite);
  66.     //End CHAPTER17MOD
  67.  
  68.     return;
  69.     }
  70.  
  71.  
  72.  
  73.  
  74. //CHAPTER17MOD
  75. /*
  76.  * CTenant::QueryInterface
  77.  * CTenant::AddRef
  78.  * CTenant::Release
  79.  *
  80.  * Purpose:
  81.  *  IUnknown members for CTenant object.
  82.  */
  83.  
  84. STDMETHODIMP CTenant::QueryInterface(REFIID riid, PPVOID ppv)
  85.     {
  86.     *ppv=NULL;
  87.  
  88.     if (IID_IUnknown==riid)
  89.         *ppv=this;
  90.  
  91.     if (IID_IOleClientSite==riid)
  92.         *ppv=m_pImpIOleClientSite;
  93.  
  94.     if (IID_IAdviseSink==riid)
  95.         *ppv=m_pImpIAdviseSink;
  96.  
  97.     if (NULL!=*ppv)
  98.         {
  99.         ((LPUNKNOWN)*ppv)->AddRef();
  100.         return NOERROR;
  101.         }
  102.  
  103.     return ResultFromScode(E_NOINTERFACE);
  104.     }
  105.  
  106.  
  107. STDMETHODIMP_(ULONG) CTenant::AddRef(void)
  108.     {
  109.     return ++m_cRef;
  110.     }
  111.  
  112. STDMETHODIMP_(ULONG) CTenant::Release(void)
  113.     {
  114.     if (0!=--m_cRef)
  115.         return m_cRef;
  116.  
  117.     delete this;
  118.     return 0;
  119.     }
  120.  
  121. //End CHAPTER17MOD
  122.  
  123.  
  124.  
  125.  
  126.  
  127. /*
  128.  * CTenant::GetID
  129.  *
  130.  * Return Value:
  131.  *  DWORD           dwID field in this tenant.
  132.  */
  133.  
  134. DWORD CTenant::GetID(void)
  135.     {
  136.     return m_dwID;
  137.     }
  138.  
  139.  
  140.  
  141. /*
  142.  * CTenant::GetStorageName
  143.  *
  144.  * Parameters:
  145.  *  pszName         LPOLESTR to a buffer in which to store the storage
  146.  *                  name for this tenant.
  147.  *
  148.  * Return Value:
  149.  *  UINT            Number of characters stored.
  150.  */
  151.  
  152. UINT CTenant::GetStorageName(LPOLESTR pszName)
  153.     {
  154.    #ifdef WIN32ANSI
  155.     char        szTemp[32];
  156.     UINT        cch;
  157.  
  158.     cch=wsprintf(szTemp, "Tenant %lu", m_dwID);
  159.     MultiByteToWideChar(CP_ACP, 0, szTemp, -1, pszName, 32);
  160.     return cch;
  161.    #else
  162.     return wsprintf(pszName, TEXT("Tenant %lu"), m_dwID);
  163.    #endif
  164.     }
  165.  
  166.  
  167.  
  168. //CHAPTER17MOD
  169. /*
  170.  * CTenant::StorageGet
  171.  *
  172.  * Purpose:
  173.  *  Returns the IStorage pointer maintained by this tenant,
  174.  *  AddRef'd of course.
  175.  *
  176.  * Parameters:
  177.  *  ppStg           LPSTORAGE * in which to return the pointer.
  178.  *
  179.  * Return Value:
  180.  *  None
  181.  */
  182.  
  183. void CTenant::StorageGet(LPSTORAGE *ppStg)
  184.     {
  185.     if (NULL==ppStg)
  186.         return;
  187.  
  188.     *ppStg=m_pIStorage;
  189.  
  190.     if (NULL!=*ppStg)
  191.         (*ppStg)->AddRef();
  192.  
  193.     return;
  194.     }
  195. //End CHAPTER17MOD
  196.  
  197.  
  198.  
  199.  
  200. /*
  201.  * CTenant::Create
  202.  *
  203.  * Purpose:
  204.  *  Creates a new tenant of the given CLSID, which can be either a
  205.  *  static bitmap or metafile or any compound document object.
  206.  *
  207.  * Parameters:
  208.  *  tType           TENANTTYPE to create, either a static metafile,
  209.  *                  bitmap, or some kind of compound document object
  210.  *                  This determines which OleCreate* call we use.
  211.  *  pvType          LPVOID providing the relevant pointer from which
  212.  *                  to create the tenant, depending on iType.
  213.  *  pFE             LPFORMATETC specifying the type of renderings
  214.  *                  to use.
  215.  *  pptl            PPOINTL in which we store offset coordinates.
  216.  *  pszl            LPSIZEL where this object should store its
  217.  *                  lometric extents.
  218.  *  pIStorage       LPSTORAGE of the page we live in.  We have to
  219.  *                  create another storage in this for the tenant.
  220.  *  ppo             PPATRONOBJECT containing placement data.
  221.  *  dwData          DWORD with extra data, sensitive to iType.
  222.  *
  223.  * Return Value:
  224.  *  UINT            A CREATE_* value depending on what we
  225.  *                  actually do.
  226.  */
  227.  
  228. UINT CTenant::Create(TENANTTYPE tType, LPVOID pvType
  229.     , LPFORMATETC pFE, PPOINTL pptl, LPSIZEL pszl
  230.     , LPSTORAGE pIStorage, PPATRONOBJECT ppo, DWORD dwData)
  231.     {
  232.     HRESULT             hr;
  233.     LPUNKNOWN           pObj;
  234.     UINT                uRet=CREATE_GRAPHICONLY;
  235.     //CHAPTER17MOD
  236.     //Some things moves to ObjectInitialize
  237.     //End CHAPTER17MOD
  238.  
  239.     if (NULL==pvType || NULL==pIStorage)
  240.         return CREATE_FAILED;
  241.  
  242.     //Fail if this is called for an already living tenant.
  243.     if (m_fInitialized)
  244.         return CREATE_FAILED;
  245.  
  246.     m_fInitialized=TRUE;
  247.  
  248.     //Create a new storage for this tenant.
  249.     if (!Open(pIStorage))
  250.         return CREATE_FAILED;
  251.  
  252.     /*
  253.      * Get the placement info if it's here.  We either have a non-
  254.      * NULL PPATRONOBJECT in ppo or we have to use default
  255.      * placement and retrieve the size from the object itself.
  256.      */
  257.     pszl->cx=0;
  258.     pszl->cy=0;
  259.  
  260.     if (NULL!=ppo)
  261.         {
  262.         *pFE=ppo->fe;
  263.         *pptl=ppo->ptl;
  264.         *pszl=ppo->szl;     //Could be 0,0 , so we ask object
  265.  
  266.         uRet=CREATE_PLACEDOBJECT;
  267.         }
  268.  
  269.     hr=ResultFromScode(E_FAIL);
  270.  
  271.     //Now create an object based specifically for the type.
  272.     switch (tType)
  273.         {
  274.         case TENANTTYPE_NULL:
  275.             break;
  276.  
  277.         case TENANTTYPE_STATIC:
  278.             /*
  279.              * We could use OleCreateStaticFromData here which does
  280.              * pretty much what we're doing below.  However, it does
  281.              * not allow us to control whether we paste a bitmap or
  282.              * a metafile--it uses metafile first, bitmap second.
  283.              * For this reason we'll use code developed in Chapter
  284.              * 11's FreeLoader to affect the paste.
  285.              */
  286.             hr=CreateStatic((LPDATAOBJECT)pvType, pFE, &pObj);
  287.             break;
  288.  
  289.         //CHAPTER17MOD
  290.         case TENANTTYPE_EMBEDDEDOBJECT:
  291.             hr=OleCreate(*((LPCLSID)pvType), IID_IUnknown
  292.                 , OLERENDER_DRAW, NULL, NULL, m_pIStorage
  293.                 , (PPVOID)&pObj);
  294.             break;
  295.  
  296.         case TENANTTYPE_EMBEDDEDFILE:
  297.             hr=OleCreateFromFile(CLSID_NULL, (LPTSTR)pvType
  298.                 , IID_IUnknown, OLERENDER_DRAW, NULL, NULL
  299.                 , m_pIStorage, (PPVOID)&pObj);
  300.             break;
  301.  
  302.         case TENANTTYPE_EMBEDDEDOBJECTFROMDATA:
  303.             hr=OleCreateFromData((LPDATAOBJECT)pvType, IID_IUnknown
  304.                 , OLERENDER_DRAW, NULL, NULL, m_pIStorage
  305.                 , (PPVOID)&pObj);
  306.             break;
  307.         //End CHAPTER17MOD
  308.  
  309.         default:
  310.             break;
  311.         }
  312.  
  313.     //If creation didn't work, get rid of the element Open created.
  314.     if (FAILED(hr))
  315.         {
  316.         Destroy(pIStorage);
  317.         return CREATE_FAILED;
  318.         }
  319.  
  320.     //CHAPTER17MOD
  321.     //We don't get the size if PatronObject data was seen already.
  322.     if (!ObjectInitialize(pObj, pFE, dwData))
  323.         {
  324.         Destroy(pIStorage);
  325.         return CREATE_FAILED;
  326.         }
  327.  
  328.     if (0==pszl->cx && 0==pszl->cy)
  329.         {
  330.         SIZEL   szl;
  331.  
  332.         //Try to get the real size of the object, default to 2"*2"
  333.         SETSIZEL((*pszl), 2*LOMETRIC_PER_INCH, 2*LOMETRIC_PER_INCH);
  334.         hr=ResultFromScode(E_FAIL);
  335.  
  336.         //Try IViewObject2 first, then IOleObject as a backup.
  337.         if (NULL!=m_pIViewObject2)
  338.             {
  339.             hr=m_pIViewObject2->GetExtent(m_fe.dwAspect, -1, NULL
  340.                 , &szl);
  341.             }
  342.         else
  343.             {
  344.             if (NULL!=m_pIOleObject)
  345.                 hr=m_pIOleObject->GetExtent(m_fe.dwAspect, &szl);
  346.             }
  347.  
  348.         if (SUCCEEDED(hr))
  349.             {
  350.             //Convert HIMETRIC to our LOMETRIC mapping
  351.             SETSIZEL((*pszl), szl.cx/10, szl.cy/10);
  352.             }
  353.         }
  354.     //End CHAPTER17MOD
  355.  
  356.     return uRet;
  357.     }
  358.  
  359.  
  360.  
  361.  
  362.  
  363.  
  364. /*
  365.  * CTenant::Load
  366.  *
  367.  * Purpose:
  368.  *  Recreates the object living in this tenant in place of calling
  369.  *  FCreate.  This is used in loading as opposed to new creation.
  370.  *
  371.  * Parameters:
  372.  *  pIStorage       LPSTORAGE of the page we live in.
  373.  *  pti             PTENTANTINFO containing persistent information.
  374.  *                  The ID value in this structure is ignored.
  375.  *
  376.  * Return Value:
  377.  *  BOOL            TRUE if successful, FALSE otherwise.
  378.  */
  379.  
  380. BOOL CTenant::Load(LPSTORAGE pIStorage, PTENANTINFO pti)
  381.     {
  382.     HRESULT         hr;
  383.     LPUNKNOWN       pObj;
  384.     //CHAPTER17MOD
  385.     DWORD           dwState=TENANTSTATE_DEFAULT;
  386.     //End CHAPTER17MOD
  387.  
  388.     if (NULL==pIStorage || NULL==pti)
  389.         return FALSE;
  390.  
  391.     //CHAPTER17MOD
  392.     /*
  393.      * If we already initialized once, clean up, releasing
  394.      * everything before we attempt to reload.  This happens
  395.      * when using the Convert Dialog.
  396.      */
  397.     if (m_fInitialized)
  398.         {
  399.         //Preserve all states except open
  400.         dwState=(m_dwState & ~TENANTSTATE_OPEN);
  401.         m_cRef++;   //Prevent accidental closure
  402.  
  403.         //This should release all holds on our IStorage as well.
  404.         if (NULL!=m_pIViewObject2)
  405.             {
  406.             m_pIViewObject2->SetAdvise(m_fe.dwAspect, 0, NULL);
  407.             ReleaseInterface(m_pIViewObject2);
  408.             }
  409.  
  410.         ReleaseInterface(m_pIOleObject);
  411.         ReleaseInterface(m_pObj);
  412.  
  413.         m_pIStorage=NULL;   //We'll have already released this.
  414.         m_cRef--;           //Match safety increment above.
  415.         }
  416.     //End CHAPTER14MOD
  417.  
  418.     m_fInitialized=TRUE;
  419.  
  420.     //Open the storage for this tenant.
  421.     if (!Open(pIStorage))
  422.         return FALSE;
  423.  
  424.     hr=OleLoad(m_pIStorage, IID_IUnknown, NULL, (PPVOID)&pObj);
  425.  
  426.     if (FAILED(hr))
  427.         {
  428.         Destroy(pIStorage);
  429.         return FALSE;
  430.         }
  431.  
  432.     //CHAPTER17MOD
  433.     m_fSetExtent=pti->fSetExtent;
  434.     ObjectInitialize(pObj, &pti->fe, NULL);
  435.  
  436.     //Restore the original state before reloading.
  437.     m_dwState=dwState;
  438.     //End CHAPTER17MOD
  439.  
  440.     RectSet(&pti->rcl, FALSE, FALSE);
  441.     return TRUE;
  442.     }
  443.  
  444.  
  445.  
  446.  
  447. /*
  448.  * CTenant::GetInfo
  449.  *
  450.  * Purpose:
  451.  *  Retrieved a TENANTINFO structure for this tenant.
  452.  *
  453.  * Parameters:
  454.  *  pti             PTENANTINFO structure to fill
  455.  *
  456.  * Return Value:
  457.  *  None
  458.  */
  459.  
  460. void CTenant::GetInfo(PTENANTINFO pti)
  461.     {
  462.     if (NULL!=pti)
  463.         {
  464.         pti->dwID=m_dwID;
  465.         pti->rcl=m_rcl;
  466.         pti->fe=m_fe;
  467.         pti->fSetExtent=m_fSetExtent;
  468.         }
  469.  
  470.     return;
  471.     }
  472.  
  473.  
  474.  
  475.  
  476. //CHAPTER17MOD
  477. /*
  478.  * CTenant::ObjectInitialize
  479.  * (Protected)
  480.  *
  481.  * Purpose:
  482.  *  Performs operations necessary after creating an object or
  483.  *  reloading one from storage.
  484.  *
  485.  * Parameters:
  486.  *  pObj            LPUNKNOWN of the object in this tenant.
  487.  *  pFE             LPFORMATETC describing the graphic here.
  488.  *  dwData          DWORD extra data.  If pFE->dwAspect==
  489.  *                  DVASPECT_ICON then this is the iconic metafile.
  490.  *
  491.  * Return Value:
  492.  *  BOOL            TRUE if the function succeeded, FALSE otherwise.
  493.  */
  494.  
  495. BOOL CTenant::ObjectInitialize(LPUNKNOWN pObj, LPFORMATETC pFE
  496.     , DWORD dwData)
  497.     {
  498.     HRESULT         hr;
  499.     LPPERSIST       pIPersist=NULL;
  500.     DWORD           dw;
  501.     PCDocument      pDoc;
  502.     TCHAR           szFile[CCHPATHMAX];
  503.  
  504.     if (NULL==pObj || NULL==pFE)
  505.         return FALSE;
  506.  
  507.     m_pObj=pObj;
  508.     m_fe=*pFE;
  509.     m_fe.ptd=NULL;
  510.     m_dwState=TENANTSTATE_DEFAULT;
  511.  
  512.     /*
  513.      * Determine the type:  Static or Embedded.  If Static,
  514.      * this will have CLSID_Picture_Metafile or CLSID_Picture_Dib.
  515.      * Otherwise it's embedded.  Later we'll add a case for links.
  516.      */
  517.     m_tType=TENANTTYPE_EMBEDDEDOBJECT;
  518.  
  519.     if (SUCCEEDED(pObj->QueryInterface(IID_IPersist
  520.         , (PPVOID)&pIPersist)))
  521.         {
  522.         CLSID   clsid=CLSID_NULL;
  523.  
  524.         hr=pIPersist->GetClassID(&clsid);
  525.  
  526.         //If we don't have a CLSID, default to static
  527.         if (FAILED(hr) || CLSID_Picture_Metafile==clsid
  528.             || CLSID_Picture_Dib==clsid)
  529.             m_tType=TENANTTYPE_STATIC;
  530.  
  531.         pIPersist->Release();
  532.         }
  533.  
  534.  
  535.     m_pIViewObject2=NULL;
  536.     hr=pObj->QueryInterface(IID_IViewObject2
  537.         , (PPVOID)&m_pIViewObject2);
  538.  
  539.     if (FAILED(hr))
  540.         return FALSE;
  541.  
  542.     m_pIViewObject2->SetAdvise(m_fe.dwAspect, 0, m_pImpIAdviseSink);
  543.  
  544.     //We need an IOleObject most of the time, so get one here.
  545.     m_pIOleObject=NULL;
  546.     hr=pObj->QueryInterface(IID_IOleObject
  547.          , (PPVOID)&m_pIOleObject);
  548.  
  549.     /*
  550.      * Follow up object creation with advises and so forth.  If
  551.      * we cannot get IOleObject here, then we know we can't do
  552.      * any IOleObject actions from here on--object is static.
  553.      */
  554.     if (FAILED(hr))
  555.         return TRUE;
  556.  
  557.     /*
  558.      * Get the MiscStatus bits and check for OLEMISC_ONLYICONIC.
  559.      * If set, force dwAspect in m_fe to DVASPECT_ICON so we
  560.      * remember to draw it properly and do extents right.
  561.      */
  562.     m_pIOleObject->GetMiscStatus(m_fe.dwAspect, &m_grfMisc);
  563.  
  564.     if (OLEMISC_ONLYICONIC & m_grfMisc)
  565.         m_fe.dwAspect=DVASPECT_ICON;
  566.  
  567.     /*
  568.      * We could pass m_pImpIOleClientSite in an OleCreate* call, but
  569.      * since this function could be called after OleLoad, we still
  570.      * need to do this here, so it's always done here...
  571.      */
  572.     m_pIOleObject->SetClientSite(m_pImpIOleClientSite);
  573.     m_pIOleObject->Advise(m_pImpIAdviseSink, &dw);
  574.  
  575.     OleSetContainedObject(m_pIOleObject, TRUE);
  576.  
  577.     /*
  578.      * For IOleObject::SetHostNames we need the application name
  579.      * and the document name (which is passed in the object
  580.      * parameter).  The design of Patron doesn't give us nice
  581.      * structured access to the name of the document we're in, so
  582.      * I grab the parent of the Pages window (the document) and
  583.      * send it DOCM_PDOCUMENT which returns us the pointer.
  584.      * Roundabout, but it works.
  585.      */
  586.  
  587.     pDoc=(PCDocument)SendMessage(GetParent(m_hWnd), DOCM_PDOCUMENT
  588.         , 0, 0L);
  589.  
  590.     if (NULL!=pDoc)
  591.         pDoc->FilenameGet(szFile, CCHPATHMAX);
  592.     else
  593.         szFile[0]=0;
  594.  
  595.     NotifyOfRename(szFile, NULL);
  596.  
  597.  
  598.     /*
  599.      * If we're creating an iconic aspect object and we have
  600.      * an object from the Insert Object dialog, then we need to
  601.      * store that iconic presentation in the cache, handled
  602.      * with the utility function INOLE_SwitchDisplayAspect.  In
  603.      * this case dwData is a handle to the metafile containing
  604.      * the icon.  If dwData is NULL then we depend on the
  605.      * server to provide the aspect, in which case we need
  606.      * a view advise.
  607.      */
  608.  
  609.     if (DVASPECT_ICON & m_fe.dwAspect)
  610.         {
  611.         DWORD           dw=DVASPECT_CONTENT;
  612.         IAdviseSink    *pSink;
  613.  
  614.         pSink=(NULL==dwData) ? NULL : m_pImpIAdviseSink;
  615.  
  616.         INOLE_SwitchDisplayAspect(m_pIOleObject, &dw
  617.             , DVASPECT_ICON, (HGLOBAL)(UINT)dwData, FALSE
  618.             , (NULL!=dwData), pSink, NULL);
  619.         }
  620.  
  621.     return TRUE;
  622.     }
  623. //End CHAPTER17MOD
  624.  
  625.  
  626.  
  627.  
  628. /*
  629.  * CTenant::Open
  630.  *
  631.  * Purpose:
  632.  *  Retrieves the IStorage associated with this tenant.  The
  633.  *  IStorage is owned by the tenant and thus the tenant always
  634.  *  holds a reference count.
  635.  *
  636.  *  If the storage is already open for this tenant, then this
  637.  *  function will AddRef it; therefore the caller must always
  638.  *  match an Open with a Close.
  639.  *
  640.  * Parameters:
  641.  *  pIStorage       LPSTORAGE above this tenant (which has its
  642.  *                  own storage).
  643.  *
  644.  * Return Value:
  645.  *  BOOL            TRUE if opening succeeds, FALSE otherwise.
  646.  */
  647.  
  648. BOOL CTenant::Open(LPSTORAGE pIStorage)
  649.     {
  650.     HRESULT     hr=NOERROR;
  651.     DWORD       dwMode=STGM_TRANSACTED | STGM_READWRITE
  652.                     | STGM_SHARE_EXCLUSIVE;
  653.     OLECHAR     szTemp[32];
  654.  
  655.     if (NULL==m_pIStorage)
  656.         {
  657.         if (NULL==pIStorage)
  658.             return FALSE;
  659.  
  660.         /*
  661.          * Attempt to open the storage under this ID.  If there is
  662.          * none, then create it.  In either case we end up with an
  663.          * IStorage that we either save in pPage or release.
  664.          */
  665.  
  666.         GetStorageName(szTemp);
  667.         hr=pIStorage->OpenStorage(szTemp, NULL, dwMode, NULL, 0
  668.             , &m_pIStorage);
  669.  
  670.         if (FAILED(hr))
  671.             {
  672.             hr=pIStorage->CreateStorage(szTemp, dwMode, 0, 0
  673.                 , &m_pIStorage);
  674.             }
  675.         }
  676.     else
  677.         m_pIStorage->AddRef();
  678.  
  679.     if (FAILED(hr))
  680.         return FALSE;
  681.  
  682.     m_cOpens++;
  683.  
  684.     //CHAPTER17MOD
  685.     //Create these if we don't have them already.
  686.     if (NULL==m_pImpIOleClientSite && NULL==m_pImpIAdviseSink)
  687.         {
  688.         m_pImpIOleClientSite=new CImpIOleClientSite(this, this);
  689.         m_pImpIAdviseSink=new CImpIAdviseSink(this, this);
  690.  
  691.         if (NULL==m_pImpIOleClientSite || NULL==m_pImpIAdviseSink)
  692.             return FALSE;
  693.         }
  694.     //End CHAPTER17MOD
  695.  
  696.     return TRUE;
  697.     }
  698.  
  699.  
  700.  
  701.  
  702. /*
  703.  * CTenant::Close
  704.  *
  705.  * Purpose:
  706.  *  Possibly commits the storage, then releases it reversing the
  707.  *  reference count from Open.  If the reference on the storage
  708.  *  goes to zero, the storage is forgotten.  However, the object we
  709.  *  contain is still held and as long as it's active the storage
  710.  *  remains alive.
  711.  *
  712.  * Parameters:
  713.  *  fCommit         BOOL indicating if we're to commit.
  714.  *
  715.  * Return Value:
  716.  *  None
  717.  */
  718.  
  719. void CTenant::Close(BOOL fCommit)
  720.     {
  721.     if (fCommit)
  722.         Update();
  723.  
  724.     if (NULL!=m_pIStorage)
  725.         {
  726.         m_pIStorage->Release();
  727.  
  728.         /*
  729.          * We can't use a zero reference count to know when to NULL
  730.          * this since other things might have AddRef'd the storage.
  731.          */
  732.         //CHAPTER17MOD
  733.         if (0==--m_cOpens)
  734.             {
  735.             m_pIStorage=NULL;
  736.  
  737.             //Close the object saving if necessary
  738.             if (NULL!=m_pIOleObject)
  739.                 {
  740.                 m_pIOleObject->Close(OLECLOSE_SAVEIFDIRTY);
  741.                 ReleaseInterface(m_pIOleObject);
  742.                 }
  743.  
  744.             //Release all other held pointers
  745.             if (NULL!=m_pIViewObject2)
  746.                 {
  747.                 m_pIViewObject2->SetAdvise(m_fe.dwAspect, 0, NULL);
  748.                 ReleaseInterface(m_pIViewObject2);
  749.                 }
  750.  
  751.             //We know we only hold one ref from Create or Load
  752.             ReleaseInterface(m_pObj);
  753.             }
  754.         //End CHAPTER17MOD
  755.         }
  756.  
  757.     return;
  758.     }
  759.  
  760.  
  761.  
  762.  
  763. /*
  764.  * CTenant::Update
  765.  *
  766.  * Purpose:
  767.  *  Forces a common on the page if it's open.
  768.  *
  769.  * Parameters:
  770.  *  None
  771.  *
  772.  * Return Value:
  773.  *  BOOL            TRUE if the object is open, FALSE otherwise.
  774.  */
  775.  
  776. BOOL CTenant::Update(void)
  777.     {
  778.     LPPERSISTSTORAGE    pIPS;
  779.  
  780.     if (NULL!=m_pIStorage)
  781.         {
  782.         /*
  783.          * We need to OleSave again because we might have changed
  784.          * the size or position of this tenant.  We also need to
  785.          * save the rectangle on the page, since that's not known
  786.          * to OLE.
  787.          */
  788.         m_pObj->QueryInterface(IID_IPersistStorage, (PPVOID)&pIPS);
  789.  
  790.         //This fails for static objects...so we improvise if so
  791.         if (FAILED(OleSave(pIPS, m_pIStorage, TRUE)))
  792.             {
  793.             //This is essentially what OleSave does.
  794.             WriteClassStg(m_pIStorage, m_clsID);
  795.             pIPS->Save(m_pIStorage, TRUE);
  796.             }
  797.  
  798.         pIPS->SaveCompleted(NULL);
  799.         pIPS->Release();
  800.  
  801.         m_pIStorage->Commit(STGC_DEFAULT);
  802.         }
  803.  
  804.     return FALSE;
  805.     }
  806.  
  807.  
  808.  
  809.  
  810.  
  811. /*
  812.  * CTenant::Destroy
  813.  *
  814.  * Purpose:
  815.  *  Removes this page from the given storage.  The caller should
  816.  *  eventually delete this CTenant object to free the object herein.
  817.  *  Nothing is committed when being destroyed.
  818.  *
  819.  * Parameters:
  820.  *  pIStorage       LPSTORAGE contianing this page on which to call
  821.  *                  DestroyElement
  822.  *
  823.  * Return Value:
  824.  *  None
  825.  */
  826.  
  827. void CTenant::Destroy(LPSTORAGE pIStorage)
  828.     {
  829.     OLECHAR     szTemp[32];
  830.  
  831.     if (NULL!=pIStorage)
  832.         {
  833.         //CHAPTER17MOD
  834.         if (NULL!=m_pIOleObject)
  835.             m_pIOleObject->Close(OLECLOSE_NOSAVE);
  836.         //End CHAPTER17MOD
  837.  
  838.         if (NULL!=m_pIStorage)
  839.             {
  840.             //Remove all reference/open counts on this storage.
  841.             while (0!=m_cOpens)
  842.                 {
  843.                 m_pIStorage->Release();
  844.                 m_cOpens--;
  845.                 }
  846.             }
  847.  
  848.         GetStorageName(szTemp);
  849.         pIStorage->DestroyElement(szTemp);
  850.  
  851.         m_pIStorage=NULL;
  852.         }
  853.  
  854.     return;
  855.     }
  856.  
  857.  
  858.  
  859.  
  860. /*
  861.  * CTenant::Select
  862.  *
  863.  * Purpose:
  864.  *  Selects or deselects the tenant.
  865.  *
  866.  * Parameters:
  867.  *  fSelect         BOOL indicating the new state of the tenant.
  868.  *
  869.  * Return Value:
  870.  *  None
  871.  */
  872.  
  873. void CTenant::Select(BOOL fSelect)
  874.     {
  875.     BOOL        fWasSelected;
  876.     DWORD       dwState;
  877.     RECT        rc;
  878.     HDC         hDC;
  879.  
  880.     fWasSelected=(BOOL)(TENANTSTATE_SELECTED & m_dwState);
  881.  
  882.     //Nothing to do when there's no change.
  883.     if (fWasSelected==fSelect)
  884.         return;
  885.  
  886.     dwState=m_dwState & ~TENANTSTATE_SELECTED;
  887.     m_dwState=dwState | ((fSelect) ? TENANTSTATE_SELECTED : 0);
  888.  
  889.     /*
  890.      * Draw sizing handles to show the selection state.  We convert
  891.      * things to MM_TEXT since that's what this function expects.
  892.      */
  893.  
  894.     RECTFROMRECTL(rc, m_rcl);
  895.     RectConvertMappings(&rc, NULL, TRUE);
  896.     OffsetRect(&rc, -(int)m_pPG->m_xPos, -(int)m_pPG->m_yPos);
  897.  
  898.     hDC=GetDC(m_hWnd);
  899.  
  900.     UIDrawHandles(&rc, hDC, UI_HANDLES_INSIDE
  901.         | UI_HANDLES_NOBORDER | UI_HANDLES_USEINVERSE
  902.         , CXYHANDLE, !fWasSelected);
  903.  
  904.     ReleaseDC(m_hWnd, hDC);
  905.  
  906.     if (fSelect)
  907.         m_pPG->m_fDirty=TRUE;
  908.  
  909.     return;
  910.     }
  911.  
  912.  
  913.  
  914.  
  915. //CHAPTER17MOD
  916. /*
  917.  * CTenant::ShowAsOpen
  918.  *
  919.  * Purpose:
  920.  *  Draws or removes the hatch pattern over an object.
  921.  *
  922.  * Parameters:
  923.  *  fOpen           BOOL indicating the open state of this tenant.
  924.  *
  925.  * Return Value:
  926.  *  None
  927.  */
  928.  
  929. void CTenant::ShowAsOpen(BOOL fOpen)
  930.     {
  931.     BOOL        fWasOpen;
  932.     DWORD       dwState;
  933.     RECT        rc;
  934.     HDC         hDC;
  935.  
  936.     fWasOpen=(BOOL)(TENANTSTATE_OPEN & m_dwState);
  937.  
  938.     dwState=m_dwState & ~TENANTSTATE_OPEN;
  939.     m_dwState=dwState | ((fOpen) ? TENANTSTATE_OPEN : 0);
  940.  
  941.     //If this was not open, then just hatch, otherwise repaint.
  942.     if (!fWasOpen && fOpen)
  943.         {
  944.         RECTFROMRECTL(rc, m_rcl);
  945.         RectConvertMappings(&rc, NULL, TRUE);
  946.         OffsetRect(&rc, -(int)m_pPG->m_xPos, -(int)m_pPG->m_yPos);
  947.  
  948.         hDC=GetDC(m_hWnd);
  949.         UIDrawShading(&rc, hDC, UI_SHADE_FULLRECT, 0);
  950.         ReleaseDC(m_hWnd, hDC);
  951.         }
  952.  
  953.     if (fWasOpen && !fOpen)
  954.         Repaint();
  955.  
  956.     return;
  957.     }
  958.  
  959.  
  960.  
  961.  
  962.  
  963. /*
  964.  * CTenant::ShowYourself
  965.  *
  966.  * Purpose:
  967.  *  Function that really just implements IOleClientSite::ShowObject.
  968.  *  Here we first check if the tenant is fully visible, and if so,
  969.  *  then nothing needs to happen.  Otherwise, if the upper left
  970.  *  corner of the tenant is in the upper left visible quadrant of
  971.  *  the window, we'll also consider ourselves done.  Otherwise
  972.  *  we'll put the upper left corner of the object at the upper left
  973.  *  corner of the window.
  974.  *
  975.  * Parameters:
  976.  *  None
  977.  *
  978.  * Return Value:
  979.  *  None
  980.  */
  981.  
  982. void CTenant::ShowYourself(void)
  983.     {
  984.     RECTL       rcl;
  985.     RECT        rc;
  986.     POINT       pt1, pt2;
  987.  
  988.     //Scrolling deals in device units; get our rectangle in those.
  989.     RectGet(&rcl, TRUE);
  990.  
  991.     //Get the window rectangle offset for the current scroll pos.
  992.     GetClientRect(m_hWnd, &rc);
  993.     OffsetRect(&rc, m_pPG->m_xPos, m_pPG->m_yPos);
  994.  
  995.     //Check if the object is already visible. (macro in bookguid.h)
  996.     SETPOINT(pt1, (int)rcl.left,  (int)rcl.top);
  997.     SETPOINT(pt2, (int)rcl.right, (int)rcl.bottom);
  998.  
  999.     if (PtInRect(&rc, pt1) && PtInRect(&rc, pt2))
  1000.         return;
  1001.  
  1002.     //Check if the upper left is within the upper left quadrant
  1003.     if (((int)rcl.left > rc.left
  1004.         && (int)rcl.left < ((rc.right+rc.left)/2))
  1005.         && ((int)rcl.top > rc.top
  1006.         && (int)rcl.top < ((rc.bottom+rc.top)/2)))
  1007.         return;
  1008.  
  1009.     //These are macros in INC\BOOK1632.H
  1010.     SendScrollPosition(m_hWnd, WM_HSCROLL, rcl.left-8);
  1011.     SendScrollPosition(m_hWnd, WM_VSCROLL, rcl.top-8);
  1012.     return;
  1013.     }
  1014.  
  1015.  
  1016.  
  1017. /*
  1018.  * CTenant::AddVerbMenu
  1019.  *
  1020.  * Purpose:
  1021.  *  Creates the variable verb menu item for the object in this
  1022.  *  tenant.
  1023.  *
  1024.  * Parmeters:
  1025.  *  hMenu           HMENU on which to add items.
  1026.  *  iPos            UINT position on that menu to add items.
  1027.  *
  1028.  * Return Value:
  1029.  *  None
  1030.  */
  1031.  
  1032. void CTenant::AddVerbMenu(HMENU hMenu, UINT iPos)
  1033.     {
  1034.     HMENU       hMenuTemp;
  1035.     LPOLEOBJECT pObj=m_pIOleObject;
  1036.  
  1037.     //If we're static, say we have no object.
  1038.     if (TENANTTYPE_STATIC==m_tType)
  1039.         pObj=NULL;
  1040.  
  1041.     OleUIAddVerbMenu(pObj, NULL, hMenu, iPos, IDM_VERBMIN
  1042.         , IDM_VERBMAX, TRUE, IDM_EDITCONVERT, &hMenuTemp);
  1043.  
  1044.     return;
  1045.     }
  1046.  
  1047.  
  1048.  
  1049.  
  1050. /*
  1051.  * CTenant::TypeGet
  1052.  *
  1053.  * Purpose:
  1054.  *  Returns the type of this tenant
  1055.  *
  1056.  * Parameters:
  1057.  *  None
  1058.  *
  1059.  * Return Value:
  1060.  *  TENANTTYPE      Type of the tenant.
  1061.  */
  1062.  
  1063. TENANTTYPE CTenant::TypeGet(void)
  1064.     {
  1065.     return m_tType;
  1066.     }
  1067.  
  1068.  
  1069.  
  1070.  
  1071.  
  1072.  
  1073. /*
  1074.  * CTenant::CopyEmbeddedObject
  1075.  *
  1076.  * Purpose:
  1077.  *  Copies an embedded object to the given data object (via SetData,
  1078.  *  assuming this is a data transfer object for clipboard/drag-drop)
  1079.  *  if that's what we're holding.
  1080.  *
  1081.  * Parameters:
  1082.  *  pIDataObject    LPDATAOBJECT in which to store the copy.
  1083.  *  pFE             LPFORMATETC into which to copy CFSTR_EMBEDDEDOBJECT
  1084.  *                  if we put that in the data object.
  1085.  *  pptl            PPOINTL to the pick point (NULL outside of
  1086.  *                  drag-drop);
  1087.  *
  1088.  * Return Value:
  1089.  *  None
  1090.  */
  1091.  
  1092. void CTenant::CopyEmbeddedObject(LPDATAOBJECT pIDataObject
  1093.     , LPFORMATETC pFE, PPOINTL pptl)
  1094.     {
  1095.     LPPERSISTSTORAGE    pIPS;
  1096.     STGMEDIUM           stm;
  1097.     FORMATETC           fe;
  1098.     HRESULT             hr;
  1099.     UINT                cf;
  1100.     POINTL              ptl;
  1101.     SIZEL               szl;
  1102.  
  1103.     //Can only copy embeddings.
  1104.     if (TENANTTYPE_EMBEDDEDOBJECT!=m_tType || NULL==m_pIOleObject)
  1105.         return;
  1106.  
  1107.     if (NULL==pptl)
  1108.         {
  1109.         SETPOINTL(ptl, 0, 0);
  1110.         pptl=&ptl;
  1111.         }
  1112.  
  1113.     /*
  1114.      * Create CFSTR_EMBEDDEDOBJECT.  This is simply an IStorage with
  1115.      * a copy of the embedded object in it.  The not-so-simple part
  1116.      * is getting an IStorage to stuff it in.  For this operation
  1117.      * we'll use a temporary compound file.
  1118.      */
  1119.  
  1120.     stm.pUnkForRelease=NULL;
  1121.     stm.tymed=TYMED_ISTORAGE;
  1122.     hr=StgCreateDocfile(NULL, STGM_TRANSACTED | STGM_READWRITE
  1123.         | STGM_CREATE| STGM_SHARE_EXCLUSIVE | STGM_DELETEONRELEASE
  1124.         , 0, &stm.pstg);
  1125.  
  1126.     if (FAILED(hr))
  1127.         return;
  1128.  
  1129.     m_pObj->QueryInterface(IID_IPersistStorage, (PPVOID)&pIPS);
  1130.  
  1131.     if (NOERROR==pIPS->IsDirty())
  1132.         {
  1133.         OleSave(pIPS, stm.pstg, FALSE);
  1134.         pIPS->SaveCompleted(NULL);
  1135.         }
  1136.     else
  1137.         m_pIStorage->CopyTo(0, NULL, NULL, stm.pstg);
  1138.  
  1139.     pIPS->Release();
  1140.  
  1141.     //stm.pstg now has a copy, so stuff it away.
  1142.     cf=RegisterClipboardFormat(CFSTR_EMBEDDEDOBJECT);
  1143.     SETDefFormatEtc(fe, cf, TYMED_ISTORAGE);
  1144.  
  1145.     if (SUCCEEDED(pIDataObject->SetData(&fe, &stm, TRUE)))
  1146.         *pFE=fe;
  1147.     else
  1148.         ReleaseStgMedium(&stm);
  1149.  
  1150.     stm.tymed=TYMED_HGLOBAL;
  1151.  
  1152.     /*
  1153.      * You want to make sure that if this object is iconic, that you
  1154.      * create the object descriptor with DVASPECT_ICON instead of
  1155.      * the more typical DVASPECT_CONTENT.  Also remember that
  1156.      * the pick point is in HIMETRIC.
  1157.      */
  1158.     XformSizeInPixelsToHimetric(NULL, (LPSIZEL)pptl, (LPSIZEL)&ptl);
  1159.  
  1160.     SETSIZEL(szl, (10*(m_rcl.right-m_rcl.left))
  1161.         , (10 * (m_rcl.bottom-m_rcl.top)));
  1162.  
  1163.     stm.hGlobal=INOLE_ObjectDescriptorFromOleObject
  1164.         (m_pIOleObject, m_fe.dwAspect, ptl, &szl);
  1165.  
  1166.     cf=RegisterClipboardFormat(CFSTR_OBJECTDESCRIPTOR);
  1167.     SETDefFormatEtc(fe, cf, TYMED_HGLOBAL);
  1168.  
  1169.     if (FAILED(pIDataObject->SetData(&fe, &stm, TRUE)))
  1170.         ReleaseStgMedium(&stm);
  1171.  
  1172.     return;
  1173.     }
  1174.  
  1175.  
  1176.  
  1177.  
  1178.  
  1179. /*
  1180.  * CTenant::NotifyOfRename
  1181.  *
  1182.  * Purpose:
  1183.  *  Instructs the tenant that the document was saved under a
  1184.  *  different name.  In order to keep the right compound document
  1185.  *  user interface, this tenant needs to tell its object through
  1186.  *  IOleObject::SetHostNames.
  1187.  *
  1188.  * Parameters:
  1189.  *  pszFile         LPTSTR of filename.
  1190.  *  pvReserved      LPVOID reserved for future use.
  1191.  *
  1192.  * Return Value:
  1193.  *  None
  1194.  */
  1195.  
  1196. void CTenant::NotifyOfRename(LPTSTR pszFile, LPVOID pvReserved)
  1197.     {
  1198.     TCHAR       szObj[40];
  1199.     TCHAR       szApp[40];
  1200.  
  1201.     if (NULL==m_pIOleObject)
  1202.         return;
  1203.  
  1204.     if (TEXT('\0')==*pszFile)
  1205.         {
  1206.         LoadString(m_pPG->m_hInst, IDS_UNTITLED, szObj
  1207.             , sizeof(szObj));
  1208.         }
  1209.     else
  1210.         {
  1211.         GetFileTitle(pszFile, szObj, sizeof(szObj));
  1212.  
  1213.        #ifndef WIN32
  1214.         //Force filenames to uppercase in DOS versions.
  1215.         AnsiUpper(szObj);
  1216.        #endif
  1217.         }
  1218.  
  1219.     LoadString(m_pPG->m_hInst, IDS_CAPTION, szApp, sizeof(szApp));
  1220.    #ifdef WIN32ANSI
  1221.     OLECHAR     szObjW[40], szAppW[40];
  1222.  
  1223.     MultiByteToWideChar(CP_ACP, 0, szObj, -1, szObjW, 40);
  1224.     MultiByteToWideChar(CP_ACP, 0, szApp, -1, szAppW, 40);
  1225.     m_pIOleObject->SetHostNames(szAppW, szObjW);
  1226.    #else
  1227.     m_pIOleObject->SetHostNames(szApp, szObj);
  1228.    #endif
  1229.     return;
  1230.     }
  1231.  
  1232. //End CHAPTER17MOD
  1233.  
  1234.  
  1235.  
  1236.  
  1237.  
  1238. /*
  1239.  * CTenant::Activate
  1240.  *
  1241.  * Purpose:
  1242.  *  Activates a verb on the object living in the tenant.  Does
  1243.  *  nothing for static objects.
  1244.  *
  1245.  * Parameters:
  1246.  *  iVerb           LONG of the verb to execute.
  1247.  *
  1248.  * Return Value:
  1249.  *  BOOL            TRUE if the object changed due to this verb
  1250.  *                  execution.
  1251.  */
  1252.  
  1253. BOOL CTenant::Activate(LONG iVerb)
  1254.     {
  1255.     //CHAPTER17MOD
  1256.     RECT        rc, rcH;
  1257.     CHourglass *pHour;
  1258.     SIZEL       szl;
  1259.  
  1260.     //Can't activate statics.
  1261.     if (TENANTTYPE_STATIC==m_tType || NULL==m_pIOleObject)
  1262.         {
  1263.         MessageBeep(0);
  1264.         return FALSE;
  1265.         }
  1266.  
  1267.     RECTFROMRECTL(rc, m_rcl);
  1268.     RectConvertMappings(&rc, NULL, TRUE);
  1269.     XformRectInPixelsToHimetric(NULL, &rc, &rcH);
  1270.  
  1271.     pHour=new CHourglass;
  1272.  
  1273.     //Get the server running first, then do a SetExtent, then show it
  1274.     OleRun(m_pIOleObject);
  1275.  
  1276.     if (m_fSetExtent)
  1277.         {
  1278.         SETSIZEL(szl, rcH.right-rcH.left, rcH.top-rcH.bottom);
  1279.         m_pIOleObject->SetExtent(m_fe.dwAspect, &szl);
  1280.         m_fSetExtent=FALSE;
  1281.         }
  1282.  
  1283.     m_pIOleObject->DoVerb(iVerb, NULL, m_pImpIOleClientSite, 0
  1284.         , m_hWnd, &rcH);
  1285.  
  1286.     delete pHour;
  1287.  
  1288.     //If object changes, IAdviseSink::OnViewChange will see it.
  1289.     return FALSE;
  1290.     //End CHAPTER17MOD
  1291.     }
  1292.  
  1293.  
  1294.  
  1295.  
  1296.  
  1297.  
  1298. /*
  1299.  * CTenant::Draw
  1300.  *
  1301.  * Purpose:
  1302.  *  Draws the tenant in its rectangle on the given hDC.  We assume
  1303.  *  the DC is already set up for the mapping mode in which our
  1304.  *  rectangle is expressed, since the Page we're in tells us both
  1305.  *  the rect and the hDC.
  1306.  *
  1307.  * Parameters:
  1308.  *  hDC             HDC in which to draw.  Could be a metafile,
  1309.  *                  memory DC, screen, or printer.
  1310.  *  ptd             DVTARGETDEVICE * describing the device.
  1311.  *  hIC             HDC holding an information context (printing).
  1312.  *  xOff, yOff      int offsets for the page in lometric
  1313.  *  fNoColor        BOOL indicating if we should do B & W
  1314.  *  fPrinter        BOOL indicating if we should render for a
  1315.  *                  printer.
  1316.  *
  1317.  * Return Value:
  1318.  *  None
  1319.  */
  1320.  
  1321. void CTenant::Draw(HDC hDC, DVTARGETDEVICE *ptd, HDC hIC
  1322.     , int xOff, int yOff, BOOL fNoColor, BOOL fPrinter)
  1323.     {
  1324.     HRESULT         hr;
  1325.     RECT            rc;
  1326.     RECTL           rcl;
  1327.     UINT            uMM;
  1328.  
  1329.     //CHAPTER17MOD
  1330.     //We hold IViewObject2 all the time now, so no QueryInterface
  1331.     //End CHAPTER17MOD
  1332.  
  1333.     RECTFROMRECTL(rc, m_rcl);
  1334.     OffsetRect(&rc, -xOff, -yOff);
  1335.     RECTLFROMRECT(rcl, rc);
  1336.  
  1337.     //CHAPTER17MOD
  1338.     //Repaint erases the rectangle to insure full object cleanup
  1339.     if (!fNoColor && !fPrinter)
  1340.         {
  1341.         COLORREF    cr;
  1342.         cr=SetBkColor(hDC, GetSysColor(COLOR_WINDOW));
  1343.         ExtTextOut(hDC, rc.left, rc.top, ETO_OPAQUE, &rc, NULL
  1344.             , 0, NULL);
  1345.         SetBkColor(hDC, cr);
  1346.         }
  1347.  
  1348.     //We have to use Draw since we have a target device and IC.
  1349.     hr=m_pIViewObject2->Draw(m_fe.dwAspect, -1, NULL, ptd, hIC, hDC
  1350.         , &rcl, NULL, NULL, 0);
  1351.  
  1352.     //End CHAPTER17MOD
  1353.  
  1354.     /*
  1355.      * If Draw failed, then perhaps it couldn't work for the device,
  1356.      * so try good old OleDraw as a last resort.  The code will
  1357.      * generally be OLE_E_BLANK.
  1358.      */
  1359.     if (FAILED(hr))
  1360.         OleDraw(m_pObj, m_fe.dwAspect, hDC, &rc);
  1361.  
  1362.     //CHAPTER17MOD
  1363.     if (!fPrinter
  1364.         && (TENANTSTATE_SELECTED | TENANTSTATE_OPEN) & m_dwState)
  1365.         {
  1366.         /*
  1367.          * Draw sizing handles to show the selection state.  We
  1368.          * convert things to MM_TEXT since that's what this
  1369.          * function expects.
  1370.          */
  1371.         RectConvertMappings(&rc, NULL, TRUE);
  1372.         uMM=SetMapMode(hDC, MM_TEXT);
  1373.  
  1374.         if (TENANTSTATE_SELECTED & m_dwState)
  1375.             {
  1376.             UIDrawHandles(&rc, hDC, UI_HANDLES_INSIDE
  1377.                 | UI_HANDLES_NOBORDER | UI_HANDLES_USEINVERSE
  1378.                 , CXYHANDLE, TRUE);
  1379.             }
  1380.  
  1381.         if (TENANTSTATE_OPEN & m_dwState)
  1382.             UIDrawShading(&rc, hDC, UI_SHADE_FULLRECT, 0);
  1383.  
  1384.         uMM=SetMapMode(hDC, uMM);
  1385.         }
  1386.     //End CHAPTER17MOD
  1387.  
  1388.     return;
  1389.     }
  1390.  
  1391.  
  1392.  
  1393.  
  1394.  
  1395. /*
  1396.  * CTenant::Repaint
  1397.  * CTenant::Invalidate
  1398.  *
  1399.  * Purpose:
  1400.  *  Repaints the tenant where it lies or invalidates its area
  1401.  *  for later repainting.
  1402.  *
  1403.  * Parameters:
  1404.  *  None
  1405.  *
  1406.  * Return Value:
  1407.  *  None
  1408.  */
  1409.  
  1410. void CTenant::Repaint(void)
  1411.     {
  1412.     RECT        rc;
  1413.     HDC         hDC;
  1414.  
  1415.     //CHAPTER17MOD
  1416.     /*
  1417.      * We might be asked to repaint from
  1418.      * IOleClientSite::OnShowWindow after we've switched pages if
  1419.      * our server was running. This check on m_cOpens prevents that.
  1420.      */
  1421.     if (0==m_cOpens || !m_fRepaintEnabled)
  1422.         return;
  1423.     //End CHAPTER17MOD
  1424.  
  1425.     hDC=GetDC(m_hWnd);
  1426.     SetRect(&rc, m_pPG->m_xPos, m_pPG->m_yPos, 0, 0);
  1427.     RectConvertMappings(&rc, NULL, FALSE);
  1428.  
  1429.     SetMapMode(hDC, MM_LOMETRIC);
  1430.     Draw(hDC, NULL, NULL, rc.left, rc.top, FALSE, FALSE);
  1431.  
  1432.     ReleaseDC(m_hWnd, hDC);
  1433.     return;
  1434.     }
  1435.  
  1436.  
  1437. void CTenant::Invalidate(void)
  1438.     {
  1439.     RECTL       rcl;
  1440.     RECT        rc;
  1441.  
  1442.     RectGet(&rcl, TRUE);
  1443.     RECTFROMRECTL(rc, rcl);
  1444.  
  1445.     OffsetRect(&rc, -(int)m_pPG->m_xPos, -(int)m_pPG->m_yPos);
  1446.     InvalidateRect(m_hWnd, &rc, FALSE);
  1447.  
  1448.     return;
  1449.     }
  1450.  
  1451.  
  1452.  
  1453.  
  1454. //CHAPTER17MOD
  1455. /*
  1456.  * CTenant::ObjectClassFormatAndIcon
  1457.  *
  1458.  * Purpose:
  1459.  *  Helper function for CPage::ConvertObject to retrieve necessary
  1460.  *  information about the object.
  1461.  *
  1462.  * Parameters:
  1463.  *  pClsID          LPCLSID in which to store the CLSID.
  1464.  *  pwFormat        LPWORD in which to store the clipboard format
  1465.  *                  used.
  1466.  *  ppszType        LPTSTR * in which to return a pointer to a
  1467.  *                  type string.
  1468.  *  phMetaIcon      HGLOBAL * in which to return the metafile
  1469.  *                  icon currently in use.
  1470.  *
  1471.  * Return Value:
  1472.  *  None
  1473.  */
  1474.  
  1475. void CTenant::ObjectClassFormatAndIcon(LPCLSID pClsID
  1476.     , LPWORD pwFormat, LPTSTR *ppszType, HGLOBAL *phMetaIcon
  1477.     , LPTSTR *ppszLabel)
  1478.     {
  1479.     HRESULT         hr;
  1480.     TCHAR           szType[128];
  1481.     LPDATAOBJECT    pIDataObject;
  1482.     FORMATETC       fe;
  1483.     STGMEDIUM       stm;
  1484.  
  1485.     if (TENANTTYPE_EMBEDDEDOBJECT!=m_tType || NULL==m_pIOleObject)
  1486.         return;
  1487.  
  1488.     if (NULL==pClsID || NULL==pwFormat || NULL==ppszType
  1489.         || NULL==phMetaIcon)
  1490.         return;
  1491.  
  1492.  
  1493.     /*
  1494.      * For embedded objects get the real CLSID of the object and
  1495.      * its format string.  If this fails then we can try to ask
  1496.      * the object, or we can look in the registry.
  1497.      */
  1498.  
  1499.     hr=ReadClassStg(m_pIStorage, pClsID);
  1500.  
  1501.     if (FAILED(hr))
  1502.         {
  1503.         hr=m_pIOleObject->GetUserClassID(pClsID);
  1504.  
  1505.         if (FAILED(hr))
  1506.             *pClsID=CLSID_NULL;
  1507.         }
  1508.  
  1509.  
  1510.     hr=ReadFmtUserTypeStg(m_pIStorage, pwFormat, ppszType);
  1511.  
  1512.     if (FAILED(hr))
  1513.         {
  1514.         *pwFormat=0;
  1515.         *ppszType=NULL;
  1516.  
  1517.         if (INOLE_GetUserTypeOfClass(*pClsID, 0, szType
  1518.             , sizeof(szType)))
  1519.             {
  1520.             *ppszType=INOLE_CopyString(szType);
  1521.             }
  1522.         }
  1523.  
  1524.     /*
  1525.      * Try to get the AuxUserType from the registry, using
  1526.      * the short version (registered under AuxUserType\2).
  1527.      * If that fails, just copy *ppszType.
  1528.      */
  1529.     *ppszLabel=NULL;
  1530.  
  1531.     if (INOLE_GetUserTypeOfClass(*pClsID, 2, szType
  1532.         , sizeof(szType)))
  1533.         {
  1534.         *ppszLabel=INOLE_CopyString(szType);
  1535.         }
  1536.     else
  1537.         *ppszLabel=INOLE_CopyString(*ppszType);
  1538.  
  1539.     //Get the icon for this thing, if we're iconic.
  1540.     *phMetaIcon=NULL;
  1541.  
  1542.     hr=m_pObj->QueryInterface(IID_IDataObject
  1543.         , (PPVOID)&pIDataObject);
  1544.  
  1545.     if (SUCCEEDED(hr))
  1546.         {
  1547.         SETFormatEtc(fe, CF_METAFILEPICT, DVASPECT_ICON, NULL
  1548.             , TYMED_MFPICT, -1);
  1549.         hr=pIDataObject->GetData(&fe, &stm);
  1550.         pIDataObject->Release();
  1551.  
  1552.         if (SUCCEEDED(hr))
  1553.             *phMetaIcon=stm.hGlobal;
  1554.         else
  1555.             *phMetaIcon=OleGetIconOfClass(*pClsID, NULL, TRUE);
  1556.         }
  1557.  
  1558.     return;
  1559.     }
  1560.  
  1561.  
  1562.  
  1563.  
  1564. /*
  1565.  * CTenant::SwitchOrUpdateAspect
  1566.  *
  1567.  * Purpose:
  1568.  *  Switches between DVASPECT_CONTENT and DVASPECT_ICON
  1569.  *
  1570.  * Parameters:
  1571.  *  hMetaIcon       HGLOBAL to the new icon if we're changing the
  1572.  *                  icon or switching to DVASPECT_ICON.  NULL to
  1573.  *                  change back to content.
  1574.  *  fPreserve       BOOL indicating if we're to preserve the old
  1575.  *                  aspect after changing.
  1576.  *
  1577.  * Return Value:
  1578.  *  BOOL            TRUE if anything changed, FALSE otherwise.
  1579.  */
  1580.  
  1581. BOOL CTenant::SwitchOrUpdateAspect(HGLOBAL hMetaIcon
  1582.     , BOOL fPreserve)
  1583.     {
  1584.     HRESULT     hr;
  1585.     DWORD       dwAspect;
  1586.     BOOL        fUpdate=FALSE;
  1587.  
  1588.     //Nothing to do if we're content already and there's no icon.
  1589.     if (NULL==hMetaIcon && DVASPECT_CONTENT==m_fe.dwAspect)
  1590.         return FALSE;
  1591.  
  1592.     //If we're iconic already, just cache the new icon
  1593.     if (NULL!=hMetaIcon && DVASPECT_ICON==m_fe.dwAspect)
  1594.         hr=INOLE_SetIconInCache(m_pIOleObject, hMetaIcon);
  1595.     else
  1596.         {
  1597.         //Otherwise, switch between iconic and content.
  1598.         dwAspect=(NULL==hMetaIcon) ? DVASPECT_CONTENT : DVASPECT_ICON;
  1599.  
  1600.         /*
  1601.          * Switch between aspects, where dwAspect has the new one
  1602.          * and m_fe.dwAspect will be changed in the process.
  1603.          */
  1604.         hr=INOLE_SwitchDisplayAspect(m_pIOleObject
  1605.             , &m_fe.dwAspect, dwAspect, hMetaIcon, !fPreserve
  1606.             , TRUE, m_pImpIAdviseSink, &fUpdate);
  1607.  
  1608.         if (SUCCEEDED(hr))
  1609.             {
  1610.             //Update MiscStatus for the new aspect
  1611.             m_pIOleObject->GetMiscStatus(m_fe.dwAspect, &m_grfMisc);
  1612.  
  1613.             if (fUpdate)
  1614.                 m_pIOleObject->Update();    //This repaints.
  1615.             }
  1616.         }
  1617.  
  1618.     //If we switched, update our extents.
  1619.     if (SUCCEEDED(hr))
  1620.         {
  1621.         SIZEL       szl;
  1622.  
  1623.         m_pIOleObject->GetExtent(m_fe.dwAspect, &szl);
  1624.  
  1625.         if (0 > szl.cy)
  1626.             szl.cy=-szl.cy;
  1627.  
  1628.         //Convert HIMETRIC absolute units to our LOMETRIC mapping
  1629.         if (0!=szl.cx && 0!=szl.cy)
  1630.             SETSIZEL(szl, szl.cx/10, -szl.cy/10);
  1631.  
  1632.         Invalidate();                   //Remove old aspect
  1633.         SizeSet(&szl, FALSE, FALSE);    //Change size
  1634.         Repaint();                      //Paint the new one
  1635.         }
  1636.  
  1637.     return SUCCEEDED(hr);
  1638.     }
  1639.  
  1640.  
  1641.  
  1642. /*
  1643.  * CTenant::EnableRepaint
  1644.  *
  1645.  * Purpose:
  1646.  *  Toggles whether the Repaint function does anything.  This
  1647.  *  is used during conversion/emulation of an object to disable
  1648.  *  repaints until the new object can be given the proper extents.
  1649.  *
  1650.  * Parameters:
  1651.  *  fEnable         TRUE to enable repaints, FALSE to disable.
  1652.  *
  1653.  * Return Value:
  1654.  *  None
  1655.  */
  1656.  
  1657. void CTenant::EnableRepaint(BOOL fEnable)
  1658.     {
  1659.     m_fRepaintEnabled=fEnable;
  1660.     return;
  1661.     }
  1662. //End CHAPTER17MOD
  1663.  
  1664.  
  1665.  
  1666.  
  1667.  
  1668.  
  1669.  
  1670.  
  1671. /*
  1672.  * CTenant::ObjectGet
  1673.  *
  1674.  * Purpose:
  1675.  *  Retrieves the LPUNKNOWN of the object in use by this tenant
  1676.  *
  1677.  * Parameters:
  1678.  *  ppUnk           LPUNKNOWN * in which to return the object
  1679.  *                  pointer.
  1680.  *
  1681.  * Return Value:
  1682.  *  None
  1683.  */
  1684.  
  1685. void CTenant::ObjectGet(LPUNKNOWN *ppUnk)
  1686.     {
  1687.     if (NULL!=ppUnk)
  1688.         {
  1689.         *ppUnk=m_pObj;
  1690.         m_pObj->AddRef();
  1691.         }
  1692.  
  1693.     return;
  1694.     }
  1695.  
  1696.  
  1697.  
  1698.  
  1699.  
  1700. /*
  1701.  * CTenant::FormatEtcGet
  1702.  *
  1703.  * Purpose:
  1704.  *  Retrieves the FORMATETC in use by this tenant
  1705.  *
  1706.  * Parameters:
  1707.  *  pFE             LPFORMATETC in which to store the information.
  1708.  *  fPresentation   BOOL indicating if we want the real format or
  1709.  *                  that of the presentation.
  1710.  *
  1711.  * Return Value:
  1712.  *  None
  1713.  */
  1714.  
  1715. void CTenant::FormatEtcGet(LPFORMATETC pFE, BOOL fPresentation)
  1716.     {
  1717.     if (NULL!=pFE)
  1718.         {
  1719.         *pFE=m_fe;
  1720.  
  1721.         //CHAPTER17MOD
  1722.         //If there is no format, use metafile (for embedded objects)
  1723.         if (fPresentation || 0==pFE->cfFormat)
  1724.             {
  1725.             //Don't mess with dwAspect; might be icon or content.
  1726.             pFE->cfFormat=CF_METAFILEPICT;
  1727.             pFE->tymed=TYMED_MFPICT;
  1728.             }
  1729.         //End CHAPTER17MOD
  1730.         }
  1731.  
  1732.     return;
  1733.     }
  1734.  
  1735.  
  1736.  
  1737.  
  1738.  
  1739. /*
  1740.  * CTenant::SizeGet
  1741.  * CTenant::SizeSet
  1742.  * CTenant::RectGet
  1743.  * CTenant::RectSet
  1744.  *
  1745.  * Purpose:
  1746.  *  Returns or sets the size/position of the object contained here.
  1747.  *
  1748.  * Parameters:
  1749.  *  pszl/prcl       LPSIZEL (Size) or LPRECTL (Rect) with the
  1750.  *                  extents of interest.  In Get situations,
  1751.  *                  this will receive the extents; in Set it
  1752.  *                  contains the extents.
  1753.  *  fDevice         BOOL indicating that pszl/prcl is expressed
  1754.  *                  in device units.  Otherwise it's LOMETRIC.
  1755.  *  fInformObj      (Set Only) BOOL indicating if we need to inform
  1756.  *                  the object all.
  1757.  *
  1758.  * Return Value:
  1759.  *  None
  1760.  */
  1761.  
  1762. void CTenant::SizeGet(LPSIZEL pszl, BOOL fDevice)
  1763.     {
  1764.     if (!fDevice)
  1765.         {
  1766.         pszl->cx=m_rcl.right-m_rcl.left;
  1767.         pszl->cy=m_rcl.bottom-m_rcl.top;
  1768.         }
  1769.     else
  1770.         {
  1771.         RECT        rc;
  1772.  
  1773.         SetRect(&rc, (int)(m_rcl.right-m_rcl.left)
  1774.             , (int)(m_rcl.bottom-m_rcl.top), 0, 0);
  1775.  
  1776.         RectConvertMappings(&rc, NULL, TRUE);
  1777.  
  1778.         pszl->cx=(long)rc.left;
  1779.         pszl->cy=(long)rc.top;
  1780.         }
  1781.  
  1782.     return;
  1783.     }
  1784.  
  1785. //CHAPTER17MOD
  1786. void CTenant::SizeSet(LPSIZEL pszl, BOOL fDevice, BOOL fInformObj)
  1787. //End CHAPTER17MOD
  1788.     {
  1789.     SIZEL           szl;
  1790.  
  1791.     if (!fDevice)
  1792.         {
  1793.         szl=*pszl;
  1794.         m_rcl.right =pszl->cx+m_rcl.left;
  1795.         m_rcl.bottom=pszl->cy+m_rcl.top;
  1796.         }
  1797.     else
  1798.         {
  1799.         RECT        rc;
  1800.  
  1801.         SetRect(&rc, (int)pszl->cx, (int)pszl->cy, 0, 0);
  1802.         RectConvertMappings(&rc, NULL, FALSE);
  1803.  
  1804.         m_rcl.right =(long)rc.left+m_rcl.left;
  1805.         m_rcl.bottom=(long)rc.top+m_rcl.top;
  1806.  
  1807.         SETSIZEL(szl, (long)rc.left, (long)rc.top);
  1808.         }
  1809.  
  1810.  
  1811.     //Tell OLE that this object was resized.
  1812.     //CHAPTER17MOD
  1813.     if (NULL!=m_pIOleObject && fInformObj)
  1814.         {
  1815.         HRESULT     hr;
  1816.         BOOL        fRun=FALSE;
  1817.  
  1818.         //Convert our LOMETRIC into HIMETRIC by *=10
  1819.         szl.cx*=10;
  1820.         szl.cy*=-10;    //Our size is stored negative.
  1821.  
  1822.         /*
  1823.          * If the MiscStatus bit of OLEMISC_RECOMPOSEONRESIZE
  1824.          * is set, then we need to run the object before calling
  1825.          * SetExtent to make sure it has a real chance to
  1826.          * re-render the object.  We have to update and close
  1827.          * the object as well after this happens.
  1828.          */
  1829.  
  1830.         if (OLEMISC_RECOMPOSEONRESIZE & m_grfMisc)
  1831.             {
  1832.             if (!OleIsRunning(m_pIOleObject))
  1833.                 {
  1834.                 OleRun(m_pIOleObject);
  1835.                 fRun=TRUE;
  1836.                 }
  1837.             }
  1838.  
  1839.         hr=m_pIOleObject->SetExtent(m_fe.dwAspect, &szl);
  1840.  
  1841.         /*
  1842.          * If the object is not running and it does not have
  1843.          * RECOMPOSEONRESIZE, then SetExtent fails.  Make
  1844.          * sure that we call SetExtent again (by just calling
  1845.          * SizeSet here again) when we next run the object.
  1846.          */
  1847.         if (SUCCEEDED(hr))
  1848.             {
  1849.             m_fSetExtent=FALSE;
  1850.  
  1851.             if (fRun)
  1852.                 {
  1853.                 m_pIOleObject->Update();
  1854.                 m_pIOleObject->Close(OLECLOSE_SAVEIFDIRTY);
  1855.                 }
  1856.             }
  1857.         else
  1858.             {
  1859.             if (OLE_E_NOTRUNNING==GetScode(hr))
  1860.                 m_fSetExtent=TRUE;
  1861.             }
  1862.         }
  1863.     //End CHAPTER17MOD
  1864.  
  1865.     return;
  1866.     }
  1867.  
  1868.  
  1869. void CTenant::RectGet(LPRECTL prcl, BOOL fDevice)
  1870.     {
  1871.     if (!fDevice)
  1872.         *prcl=m_rcl;
  1873.     else
  1874.         {
  1875.         RECT        rc;
  1876.  
  1877.         RECTFROMRECTL(rc, m_rcl);
  1878.         RectConvertMappings(&rc, NULL, TRUE);
  1879.         RECTLFROMRECT(*prcl, rc);
  1880.         }
  1881.  
  1882.     return;
  1883.     }
  1884.  
  1885.  
  1886. //CHAPTER17MOD
  1887. void CTenant::RectSet(LPRECTL prcl, BOOL fDevice, BOOL fInformObj)
  1888. //End CHAPTER17MOD
  1889.     {
  1890.     SIZEL   szl;
  1891.     LONG    cx, cy;
  1892.  
  1893.     cx=m_rcl.right-m_rcl.left;
  1894.     cy=m_rcl.bottom-m_rcl.top;
  1895.  
  1896.     if (!fDevice)
  1897.         m_rcl=*prcl;
  1898.     else
  1899.         {
  1900.         RECT        rc;
  1901.  
  1902.         RECTFROMRECTL(rc, *prcl);
  1903.         RectConvertMappings(&rc, NULL, FALSE);
  1904.         RECTLFROMRECT(m_rcl, rc);
  1905.         }
  1906.  
  1907.     /*
  1908.      * Tell ourselves that the size changed, if it did.  SizeSet
  1909.      * will call IOleObject::SetExtent for us.
  1910.      */
  1911.     if ((m_rcl.right-m_rcl.left)!=cx || (m_rcl.bottom-m_rcl.top)!=cy)
  1912.         {
  1913.         SETSIZEL(szl, m_rcl.right-m_rcl.left, m_rcl.bottom-m_rcl.top);
  1914.         //CHAPTER17MOD
  1915.         SizeSet(&szl, FALSE, fInformObj);
  1916.         //End CHAPTER17MOD
  1917.         }
  1918.  
  1919.     return;
  1920.     }
  1921.  
  1922.  
  1923.  
  1924.  
  1925.  
  1926.  
  1927.  
  1928. /*
  1929.  * CTenant::CreateStatic
  1930.  * (Protected)
  1931.  *
  1932.  * Purpose:
  1933.  *  Creates a new static bitmap or metafile object for this tenant
  1934.  *  using a freeloading method allowing us to specify exactly which
  1935.  *  type of data we want to paste since OleCreateStaticFromData
  1936.  *  doesn't.
  1937.  *
  1938.  * Parameters:
  1939.  *  pIDataObject    LPDATAOBJECT from which to paste.
  1940.  *  pFE             LPFORMATETC describing the format to paste.
  1941.  *  ppObj           LPUNKNOWN * into which we store the
  1942.  *                  object pointer.
  1943.  *
  1944.  * Return Value:
  1945.  *  HRESULT         NOERROR on success, error code otherwise.
  1946.  */
  1947.  
  1948. HRESULT CTenant::CreateStatic(LPDATAOBJECT pIDataObject
  1949.     , LPFORMATETC pFE, LPUNKNOWN *ppObj)
  1950.     {
  1951.     HRESULT             hr;
  1952.     STGMEDIUM           stm;
  1953.     LPUNKNOWN           pIUnknown;
  1954.     LPOLECACHE          pIOleCache;
  1955.     LPPERSISTSTORAGE    pIPersistStorage;
  1956.     CLSID               clsID;
  1957.  
  1958.     *ppObj=NULL;
  1959.  
  1960.     //Try to get the data desired as specified in pFE->cfFormat
  1961.     hr=pIDataObject->GetData(pFE, &stm);
  1962.  
  1963.     if (FAILED(hr))
  1964.         return hr;
  1965.  
  1966.     //Create the object to handle this data.
  1967.     if (CF_METAFILEPICT==pFE->cfFormat)
  1968.         clsID=CLSID_Picture_Metafile;
  1969.     else
  1970.         clsID=CLSID_Picture_Dib;
  1971.  
  1972.     hr=CreateDataCache(NULL, clsID, IID_IUnknown
  1973.         , (PPVOID)&pIUnknown);
  1974.  
  1975.     if (FAILED(hr))
  1976.         {
  1977.         ReleaseStgMedium(&stm);
  1978.         return hr;
  1979.         }
  1980.  
  1981.     m_clsID=clsID;
  1982.  
  1983.     //Stuff the data into the object
  1984.     pIUnknown->QueryInterface(IID_IPersistStorage
  1985.         , (PPVOID)&pIPersistStorage);
  1986.     pIPersistStorage->InitNew(m_pIStorage);
  1987.  
  1988.     //Now that we have the cache object, shove the data into it.
  1989.     pIUnknown->QueryInterface(IID_IOleCache, (PPVOID)&pIOleCache);
  1990.     pIOleCache->Cache(pFE, ADVF_PRIMEFIRST, NULL);
  1991.  
  1992.     hr=pIOleCache->SetData(pFE, &stm, TRUE);
  1993.     pIOleCache->Release();
  1994.  
  1995.     //Insure there is a persistent copy on the disk
  1996.     WriteClassStg(m_pIStorage, m_clsID);
  1997.     pIPersistStorage->Save(m_pIStorage, TRUE);
  1998.     pIPersistStorage->SaveCompleted(NULL);
  1999.     pIPersistStorage->Release();
  2000.  
  2001.     //The cache owns this now.
  2002.     ReleaseStgMedium(&stm);
  2003.  
  2004.     if (FAILED(hr))
  2005.         pIUnknown->Release();
  2006.     else
  2007.         *ppObj=pIUnknown;
  2008.  
  2009.     return hr;
  2010.     }
  2011.