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 / chap18 / cosmo / iperstor.cpp < prev    next >
C/C++ Source or Header  |  1995-05-03  |  15KB  |  527 lines

  1. /*
  2.  * IPERSTOR.CPP
  3.  * Cosmo Chapter 18
  4.  *
  5.  * Implementation of the IPersistStorage interface that we expose on
  6.  * the CFigure compound document object.  This ties into the
  7.  * functionality of CPolyline.
  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 "cosmo.h"
  18.  
  19.  
  20. /*
  21.  * CImpIPersistStorage:CImpIPersistStorage
  22.  * CImpIPersistStorage::~CImpIPersistStorage
  23.  *
  24.  * Constructor Parameters:
  25.  *  pObj            PCFigure associated with this object.
  26.  *  pUnkOuter       LPUNKNOWN of the controlling unknown.
  27.  */
  28.  
  29. CImpIPersistStorage::CImpIPersistStorage(PCFigure pObj
  30.     , LPUNKNOWN pUnkOuter)
  31.     {
  32.     m_cRef=0;
  33.     m_pObj=pObj;
  34.     m_pUnkOuter=pUnkOuter;
  35.     m_psState=PSSTATE_UNINIT;
  36.  
  37.     m_fConvert=FALSE;
  38.     return;
  39.     }
  40.  
  41.  
  42. CImpIPersistStorage::~CImpIPersistStorage(void)
  43.     {
  44.     return;
  45.     }
  46.  
  47.  
  48.  
  49.  
  50. /*
  51.  * CImpIPersistStorage::QueryInterface
  52.  * CImpIPersistStorage::AddRef
  53.  * CImpIPersistStorage::Release
  54.  */
  55.  
  56. STDMETHODIMP CImpIPersistStorage::QueryInterface(REFIID riid
  57.     , PPVOID ppv)
  58.     {
  59.     return m_pUnkOuter->QueryInterface(riid, ppv);
  60.     }
  61.  
  62. STDMETHODIMP_(ULONG) CImpIPersistStorage::AddRef(void)
  63.     {
  64.     ++m_cRef;
  65.     return m_pUnkOuter->AddRef();
  66.     }
  67.  
  68. STDMETHODIMP_(ULONG) CImpIPersistStorage::Release(void)
  69.     {
  70.     --m_cRef;
  71.     return m_pUnkOuter->Release();
  72.     }
  73.  
  74.  
  75.  
  76.  
  77.  
  78. /*
  79.  * CImpIPersistStorage::GetClassID
  80.  *
  81.  * Purpose:
  82.  *  Returns the CLSID of the object represented by this interface.
  83.  *
  84.  * Parameters:
  85.  *  pClsID          LPCLSID in which to store our CLSID.
  86.  *
  87.  * Return Value:
  88.  *  HRESULT         NOERROR or a general error value.
  89.  */
  90.  
  91. STDMETHODIMP CImpIPersistStorage::GetClassID(LPCLSID pClsID)
  92.     {
  93.     if (PSSTATE_UNINIT==m_psState)
  94.         return ResultFromScode(E_UNEXPECTED);
  95.  
  96.     *pClsID=m_pObj->m_clsID;
  97.     return NOERROR;
  98.     }
  99.  
  100.  
  101.  
  102.  
  103.  
  104. /*
  105.  * CImpIPersistStorage::IsDirty
  106.  *
  107.  * Purpose:
  108.  *  Tells the caller if we have made changes to this object since
  109.  *  it was loaded or initialized new.
  110.  *
  111.  * Parameters:
  112.  *  None
  113.  *
  114.  * Return Value:
  115.  *  HRESULT         Contains S_OK if we ARE dirty, S_FALSE if
  116.  *                  NOT dirty.
  117.  */
  118.  
  119. STDMETHODIMP CImpIPersistStorage::IsDirty(void)
  120.     {
  121.     if (PSSTATE_UNINIT==m_psState)
  122.         return ResultFromScode(E_UNEXPECTED);
  123.  
  124.     //CFigure::FIsDirty returns the document's dirty flag.
  125.     return ResultFromScode(m_pObj->FIsDirty() ? S_OK : S_FALSE);
  126.     }
  127.  
  128.  
  129.  
  130.  
  131.  
  132.  
  133.  
  134. /*
  135.  * CImpIPersistStorage::InitNew
  136.  *
  137.  * Purpose:
  138.  *  Provides the object with the IStorage to hold on to while the
  139.  *  object is running.  Here we initialize the structure of the
  140.  *  storage and AddRef it for incremental access. This function will
  141.  *  only be called once in the object's lifetime in lieu of Load.
  142.  *
  143.  * Parameters:
  144.  *  pIStorage       LPSTORAGE for the object.
  145.  *
  146.  * Return Value:
  147.  *  HRESULT         NOERROR or a general error value.
  148.  */
  149.  
  150. STDMETHODIMP CImpIPersistStorage::InitNew(LPSTORAGE pIStorage)
  151.     {
  152.     HRESULT     hr;
  153.  
  154.     if (PSSTATE_UNINIT!=m_psState)
  155.         return ResultFromScode(E_UNEXPECTED);
  156.  
  157.     if (NULL==pIStorage)
  158.         return ResultFromScode(E_POINTER);
  159.  
  160.     /*
  161.      * The rules of IPersistStorage mean we hold onto the IStorage
  162.      * and pre-create anything we'd need in Save(...,TRUE) for
  163.      * low-memory situations.  For us this means creating our
  164.      * "CONTENTS" stream and holding onto that IStream as
  165.      * well as the IStorage here (requiring an AddRef call).
  166.      */
  167.  
  168.     hr=pIStorage->CreateStream(SZSTREAM, STGM_DIRECT
  169.         | STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE
  170.         , 0, 0, &m_pObj->m_pIStream);
  171.  
  172.     if (FAILED(hr))
  173.         return hr;
  174.  
  175.     //We expect that the client has called WriteClassStg
  176.     WriteFmtUserTypeStg(pIStorage, m_pObj->m_cf
  177.         , (*m_pObj->m_pST)[IDS_USERTYPE]);
  178.  
  179.     pIStorage->AddRef();
  180.     m_pObj->m_pIStorage=pIStorage;
  181.  
  182.     m_psState=PSSTATE_SCRIBBLE;
  183.     return NOERROR;
  184.     }
  185.  
  186.  
  187.  
  188.  
  189.  
  190. /*
  191.  * CImpIPersistStorage::Load
  192.  *
  193.  * Purpose:
  194.  *  Instructs the object to load itself from a previously saved
  195.  *  IStorage that was handled by Save in another object lifetime.
  196.  *  This function will only be called once in the object's lifetime
  197.  *  in lieu of InitNew. The object should hold on to pIStorage here
  198.  *  for incremental access and low-memory saves in Save.
  199.  *
  200.  * Parameters:
  201.  *  pIStorage       LPSTORAGE from which to load.
  202.  *
  203.  * Return Value:
  204.  *  HRESULT         NOERROR or a general error value.
  205.  */
  206.  
  207. STDMETHODIMP CImpIPersistStorage::Load(LPSTORAGE pIStorage)
  208.     {
  209.     HRESULT     hr;
  210.     LONG        lRet;
  211.     LPSTREAM    pIStream;
  212.  
  213.     if (PSSTATE_UNINIT!=m_psState)
  214.         return ResultFromScode(E_UNEXPECTED);
  215.  
  216.     if (NULL==pIStorage)
  217.         return ResultFromScode(E_POINTER);
  218.  
  219.     //This tells us if we're coming from another class storage.
  220.     m_fConvert=(NOERROR==GetConvertStg(pIStorage));
  221.  
  222.     //This is the type of storage we're really messing with in Treat As
  223.     ReadClassStg(pIStorage, &m_pObj->m_clsID);
  224.  
  225.     hr=pIStorage->OpenStream(SZSTREAM, 0, STGM_DIRECT
  226.         | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pIStream);
  227.  
  228.     //We might be looking for OLE 1 streams as well.
  229.     if (FAILED(hr))
  230.         {
  231.         hr=pIStorage->OpenStream(SZOLE1STREAM, 0, STGM_DIRECT
  232.             | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pIStream);
  233.  
  234.         if (FAILED(hr))
  235.             return ResultFromScode(STG_E_READFAULT);
  236.  
  237.         m_pObj->m_pPL->m_fReadFromOLE10=TRUE;
  238.         }
  239.  
  240.     if (FAILED(hr))
  241.         return ResultFromScode(STG_E_READFAULT);
  242.  
  243.     lRet=m_pObj->m_pPL->ReadFromStream(pIStream);
  244.  
  245.     if (lRet < 0)
  246.         return ResultFromScode(STG_E_READFAULT);
  247.  
  248.  
  249.     /*
  250.      * We don't call pIStream->Release here because we may need
  251.      * it for a low-memory save in Save.  We also need to
  252.      * hold onto a copy of pIStorage, meaning AddRef.
  253.      */
  254.     m_pObj->m_pIStream=pIStream;
  255.  
  256.     pIStorage->AddRef();
  257.     m_pObj->m_pIStorage=pIStorage;
  258.  
  259.     m_psState=PSSTATE_SCRIBBLE;
  260.     return NOERROR;
  261.     }
  262.  
  263.  
  264.  
  265.  
  266.  
  267. /*
  268.  * CImpIPersistStorage::Save
  269.  *
  270.  * Purpose:
  271.  * Purpose:
  272.  *  Saves the data for this object to an IStorage which may
  273.  *  or may not be the same as the one previously passed to
  274.  *  Load, indicated with fSameAsLoad.  After this call we may
  275.  *  not write into the storage again until SaveCompleted is
  276.  *  called, although we may still read.
  277.  *
  278.  * Parameters:
  279.  *  pIStorage       LPSTORAGE in which to save our data.
  280.  *  fSameAsLoad     BOOL indicating if this is the same pIStorage
  281.  *                  that was passed to Load.  If TRUE, then the
  282.  *                  object should write whatever it has *without
  283.  *                  *using any extra memory* as this may be a low
  284.  *                  memory save attempt.  That means that you must
  285.  *                  not try to open or create streams.  If FALSE
  286.  *                  you need to regenerate your whole storage
  287.  *                  structure, being sure to also release any
  288.  *                  pointers held from InitNew and Load.
  289.  *
  290.  * Return Value:
  291.  *  HRESULT         NOERROR or a general error value.
  292.  */
  293.  
  294. STDMETHODIMP CImpIPersistStorage::Save(LPSTORAGE pIStorage
  295.     , BOOL fSameAsLoad)
  296.     {
  297.     LONG        lRet;
  298.     HRESULT     hr;
  299.     LPSTREAM    pIStream;
  300.     LONG        lVer=VERSIONCURRENT;
  301.  
  302.     //Have to come here from scribble state.
  303.     if (PSSTATE_SCRIBBLE!=m_psState)
  304.         return ResultFromScode(E_UNEXPECTED);
  305.  
  306.     //Must have an IStorage if we're not in SameAsLoad
  307.     if (NULL==pIStorage && !fSameAsLoad)
  308.         return ResultFromScode(E_POINTER);
  309.  
  310.     /*
  311.      * If this was read from an OLE 1.0 storage, but there is no
  312.      * convert bit, then we have to save the 1.0 format to the
  313.      * "\1Ole10Native" stream.  Otherwise if we were converting
  314.      * from OLE 1.0, we should nuke the stream since it's no longer
  315.      * useful.  To handle this, we call WriteToStorage with
  316.      * VERSIONCURRENT in any convert case.  WriteToStorage will
  317.      * remove the OLE 1.0 stream if it previously read from one, or
  318.      * it will just save normally.
  319.      *
  320.      * The Polyine allows us to look at it's m_fReadFromOLE10 which
  321.      * tells us to pass it 0x00010000 if we're not converting, that
  322.      * is, we're doing Treat As on the OLE 1.0 object and so we
  323.      * need to write the new data in the Ole10Native stream.
  324.      */
  325.  
  326.     if (!m_fConvert && m_pObj->m_pPL->m_fReadFromOLE10)
  327.         lVer=0x00010000;
  328.  
  329.  
  330.     /*
  331.      * If we're saving to a new storage, create a new stream.
  332.      * If fSameAsLoad it TRUE, then we write to the
  333.      * stream we already allocated.  We should NOT depends on
  334.      * pIStorage with fSameAsLoad is TRUE.
  335.      *
  336.      * If we're converting an OLE 1 storage to an OLE 2 storage,
  337.      * then we have to create a new stream (conversion is not
  338.      * guaranteed to succeed in low memory) and delete the old
  339.      * one, so we ignore fSameAsLoad if we're converting.
  340.      */
  341.  
  342.     if (fSameAsLoad
  343.         && !(m_pObj->m_pPL->m_fReadFromOLE10 && m_fConvert))
  344.         {
  345.         LARGE_INTEGER   li;
  346.  
  347.         /*
  348.          * Use pre-allocated streams to avoid failures due
  349.          * to low-memory conditions.  Be sure to reset the
  350.          * stream pointer if you used this stream before!!
  351.          */
  352.         pIStream=m_pObj->m_pIStream;
  353.         LISet32(li, 0);
  354.         pIStream->Seek(li, STREAM_SEEK_SET, NULL);
  355.  
  356.         //This matches the Release below.
  357.         pIStream->AddRef();
  358.         }
  359.     else
  360.         {
  361.         hr=pIStorage->CreateStream(SZSTREAM, STGM_DIRECT
  362.             | STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE
  363.             , 0, 0, &pIStream);
  364.  
  365.         if (FAILED(hr))
  366.             return hr;
  367.  
  368.         WriteFmtUserTypeStg(pIStorage, m_pObj->m_cf
  369.             , (*m_pObj->m_pST)[IDS_USERTYPE]);
  370.         }
  371.  
  372.     lRet=m_pObj->m_pPL->WriteToStream(pIStream, lVer);
  373.     pIStream->Release();
  374.  
  375.     /*
  376.      * If we are overwriting an OLE 1 storage, delete the old
  377.      * Ole10Native stream if writing our CONTENTS worked.
  378.      */
  379.     if (m_pObj->m_pPL->m_fReadFromOLE10 && m_fConvert && (lRet >= 0))
  380.         pIStorage->DestroyElement(SZOLE1STREAM);
  381.  
  382.     //Clear the convert bit if it was set
  383.     if (m_fConvert)
  384.         {
  385.         UINT        cf;
  386.  
  387.         cf=RegisterClipboardFormat((*m_pObj->m_pST)[IDS_FORMAT]);
  388.         WriteFmtUserTypeStg(pIStorage, cf
  389.             , (*m_pObj->m_pST)[IDS_USERTYPE]);
  390.  
  391.         SetConvertStg(pIStorage, FALSE);
  392.         m_fConvert=FALSE;
  393.         }
  394.  
  395.     if (lRet >= 0)
  396.         {
  397.         m_psState=PSSTATE_ZOMBIE;
  398.         return NOERROR;
  399.         }
  400.  
  401.     return ResultFromScode(STG_E_WRITEFAULT);
  402.     }
  403.  
  404.  
  405.  
  406.  
  407.  
  408.  
  409. /*
  410.  * CImpIPersistStorage::SaveCompleted
  411.  *
  412.  * Purpose:
  413.  *  Notifies the object that the storage in pIStorage has been
  414.  *  completely saved now.  This is called when the user of this
  415.  *  object wants to save us in a completely new storage, and if
  416.  *  we normally hang on to the storage we have to reinitialize
  417.  *  ourselves here for this new one that is now complete.
  418.  *
  419.  * Parameters:
  420.  *  pIStorage       LPSTORAGE of the new storage in which we live.
  421.  *
  422.  * Return Value:
  423.  *  HRESULT         NOERROR or a general error value.
  424.  */
  425.  
  426. STDMETHODIMP CImpIPersistStorage::SaveCompleted(LPSTORAGE pIStorage)
  427.     {
  428.     HRESULT     hr;
  429.     LPSTREAM    pIStream;
  430.  
  431.     //Must be called in no-scribble or hands-off state
  432.     if (!(PSSTATE_ZOMBIE==m_psState || PSSTATE_HANDSOFF==m_psState))
  433.         return ResultFromScode(E_UNEXPECTED);
  434.  
  435.     //If we're coming from Hands-Off, we'd better get a storage
  436.     if (NULL==pIStorage && PSSTATE_HANDSOFF==m_psState)
  437.         return ResultFromScode(E_UNEXPECTED);
  438.  
  439.     /*
  440.      * If pIStorage is NULL, then we don't need to do anything
  441.      * since we already have all the pointers we need for Save.
  442.      * Otherwise we have to release any held pointers and
  443.      * reinitialize them from pIStorage.
  444.      */
  445.  
  446.     if (NULL!=pIStorage)
  447.         {
  448.         hr=pIStorage->OpenStream(SZSTREAM, 0, STGM_DIRECT
  449.             | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0
  450.             , &pIStream);
  451.  
  452.         if (FAILED(hr))
  453.             return hr;
  454.  
  455.         if (NULL!=m_pObj->m_pIStream)
  456.             m_pObj->m_pIStream->Release();
  457.  
  458.         m_pObj->m_pIStream=pIStream;
  459.  
  460.         if (NULL!=m_pObj->m_pIStorage)
  461.             m_pObj->m_pIStorage->Release();
  462.  
  463.         m_pObj->m_pIStorage=pIStorage;
  464.         m_pObj->m_pIStorage->AddRef();
  465.         }
  466.  
  467.     m_pObj->SendAdvise(OBJECTCODE_SAVED);
  468.     m_psState=PSSTATE_SCRIBBLE;
  469.     return NOERROR;
  470.     }
  471.  
  472.  
  473.  
  474.  
  475.  
  476. /*
  477.  * CImpIPersistStorage::HandsOffStorage
  478.  *
  479.  * Purpose:
  480.  * Purpose:
  481.  *  Instructs the object that another agent is interested in having
  482.  *  total access to the storage we might be hanging on to from
  483.  *  InitNew or SaveCompleted.  In this case we must release our hold
  484.  *  and await another call to SaveCompleted before we have a hold
  485.  *  again.  Therefore we cannot read or write after this call until
  486.  *  SaveCompleted.
  487.  *
  488.  *  Situations where this might happen arise in compound document
  489.  *  scenarios where this object might be in-place active but the
  490.  *  application wants to rename and commit the root storage.
  491.  *  Therefore we are asked to close our hold, let the container
  492.  *  party on the storage, then call us again later to tell us the
  493.  *  new storage we can hold.
  494.  *
  495.  * Parameters:
  496.  *  None
  497.  *
  498.  * Return Value:
  499.  *  HRESULT         NOERROR or a general error value.
  500.  */
  501.  
  502. STDMETHODIMP CImpIPersistStorage::HandsOffStorage(void)
  503.     {
  504.     /*
  505.      * Must come from scribble or no-scribble.  A repeated call
  506.      * to HandsOffStorage is an unexpected error (bug in client).
  507.      */
  508.     if (PSSTATE_UNINIT==m_psState || PSSTATE_HANDSOFF==m_psState)
  509.         return ResultFromScode(E_UNEXPECTED);
  510.  
  511.     //Release held pointers
  512.     if (NULL!=m_pObj->m_pIStream)
  513.         {
  514.         m_pObj->m_pIStream->Release();
  515.         m_pObj->m_pIStream=NULL;
  516.         }
  517.  
  518.     if (NULL!=m_pObj->m_pIStorage)
  519.         {
  520.         m_pObj->m_pIStorage->Release();
  521.         m_pObj->m_pIStorage=NULL;
  522.         }
  523.  
  524.     m_psState=PSSTATE_HANDSOFF;
  525.     return NOERROR;
  526.     }
  527.