text: //=--------------------------------------------------------------------------=
  // CtlPsst.Cpp
  //=--------------------------------------------------------------------------=
  // Copyright 1995-1996 Microsoft Corporation.  All Rights Reserved.
  //
  // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 
  // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 
  // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 
  // PARTICULAR PURPOSE.
  //=--------------------------------------------------------------------------=
  //
  // implementation of persistence interfaces for COleControl.
  //
  #include "IPServer.H"
  #include "CtrlObj.H"
  
  #include "CtlHelp.H"
  #include "Util.H"
  
  // this is the name of the stream we'll save our ole controls to.
  //
  const WCHAR wszCtlSaveStream [] = L"CONTROLSAVESTREAM";
  
  // for ASSERT and FAIL
  //
  SZTHISFILE
  
  //=--------------------------------------------------------------------------=
  // to help with out stream save implementation ...
  //
  #define STREAMHDR_SIGNATURE 0x12344321  // Signature to identify our format (avoid crashes!)
  #define IPROP_END 0xFF                  // Marker at end of property list
  #define MAXAUTOBUF 3800                 // Best if < 1 page.
  
  typedef struct tagSTREAMHDR {
  
      DWORD  dwSignature;     // Signature.
      size_t cbWritten;       // Number of bytes written
  
  } STREAMHDR;
  
  //=--------------------------------------------------------------------------=
  // COleControl persistence interfaces
  //=--------------------------------------------------------------------------=
  
  
  //=--------------------------------------------------------------------------=
  // COleControl::Load    [IPersistPropertyBag]
  //=--------------------------------------------------------------------------=
  // IPersistPropertyBag.  we've got a property bag, so let's load our properties
  // from it.
  //
  // Parameters:
  //    IPropertyBag *      - [in] pbag from which to read props.
  //    IErrorLog *         - [in] error log to write to
  //
  // Output:
  //    HRESULT
  //
  // Notes:
  //
  STDMETHODIMP COleControl::Load
  (
      IPropertyBag *pPropertyBag,
      IErrorLog    *pErrorLog
  )
  {
      HRESULT hr;
  
      // load in our standard state first.  nothing serious here ... currently,
      // we've just got two properties, for cx and cy.
      //
      hr = LoadStandardState(pPropertyBag, pErrorLog);
      RETURN_ON_FAILURE(hr);
  
      // now call the user text load function, and get them to load in whatever
      // they're interested in.
      //
      hr = LoadTextState(pPropertyBag, pErrorLog);
  
      return hr;
  }
  
  //=--------------------------------------------------------------------------=
  // COleControl::Save    [IPersistPropertyBag]
  //=--------------------------------------------------------------------------=
  // given a property bag, save out all the relevant state information.
  //
  // Parameters:
  //    IPropertyBag *        - [in] property to write to
  //    BOOL                  - [in] do we clear the dirty bit?
  //    BOOL                  - [in] do we write out default values anyhoo?
  //
  // Output:
  //    HRESULT
  //
  // Notes:
  //
  STDMETHODIMP COleControl::Save
  (
      IPropertyBag *pPropertyBag,
      BOOL          fClearDirty,
      BOOL          fWriteDefault
  )
  {
      HRESULT hr;
  
      // save out standard state information
      //
      hr = SaveStandardState(pPropertyBag);
      RETURN_ON_FAILURE(hr);
  
      // now call the user function and get them to save out
      // all of their properties.
      //
      hr = SaveTextState(pPropertyBag, fWriteDefault);
      RETURN_ON_FAILURE(hr);
  
      // now clear the dirty flag and send out notification that we're
      // done.
      //
      if (fClearDirty)
          m_fDirty = FALSE;
  
      if (m_pOleAdviseHolder)
          m_pOleAdviseHolder->SendOnSave();
  
      return S_OK;
  }
  
  //=--------------------------------------------------------------------------=
  // COleControl::GetClassID    [IPersistStreamInit]
  //=--------------------------------------------------------------------------=
  // returns the classid of this mamma
  //
  // Parameters:
  //    CLSID *         - [out] where to put the clsid
  //
  // Output:
  //    HRESULT
  //
  // Notes:
  //
  STDMETHODIMP COleControl::GetClassID
  (
      CLSID *pclsid
  )
  {
      CHECK_POINTER(pclsid);
  
      // copy the thing over
      //
      *pclsid = CLSIDOFOBJECT(m_ObjectType);
      return S_OK;
  }
  
  
  //=--------------------------------------------------------------------------=
  // COleControl::IsDirty    [IPersistStreamInit]
  //=--------------------------------------------------------------------------=
  // asks if we're dirty or not.  duh.
  //
  // Output:
  //    HRESULT        - S_OK: dirty, S_FALSE: not dirty
  //
  // Notes:
  //
  STDMETHODIMP COleControl::IsDirty
  (
      void
  )
  {
      return (m_fDirty) ? S_OK : S_FALSE;
  }
  
  //=--------------------------------------------------------------------------=
  // COleControl::InitNew    [IPersistStreamInit]
  //=--------------------------------------------------------------------------=
  // causes the control to intialize itself with a new bunch of state information
  //
  // Output:
  //    HRESULT
  //
  // Notes:
  //
  STDMETHODIMP COleControl::InitNew
  (
      void
  )
  {
      BOOL f;
  
      // call the overridable function to do this work
      //
      f = InitializeNewState();
      return (f) ? S_OK : E_FAIL;
  }
  
  //=--------------------------------------------------------------------------=
  // COleControl::GetSizeMax    [IPersistStreamInit]
  //=--------------------------------------------------------------------------=
  //
  // Parameters:
  //    ULARGE_INTEGER *    - [out]
  //
  // Output:
  //    HRESULT
  //
  // Notes:
  //
  STDMETHODIMP COleControl::GetSizeMax
  (
      ULARGE_INTEGER *pulMaxSize
  )
  {
      return E_NOTIMPL;
  }
  
  //=--------------------------------------------------------------------------=
  // COleControl::Load    [IPersistStreamInit]
  //=--------------------------------------------------------------------------=
  // load from an IStream
  //
  // Parameters:
  //    IStream *    - [in] stream from which to load
  //
  // Output:
  //    HRESULT
  //
  // Notes:
  //
  STDMETHODIMP COleControl::Load
  (
      IStream *pStream
  )
  {
      HRESULT hr;
  
      // first thing to do is read in standard properties the user don't
      // persist themselves.
      //
      hr = LoadStandardState(pStream);
      RETURN_ON_FAILURE(hr);
  
      // load in the user properties.  this method is one they -have- to implement
      // themselves.
      //
      hr = LoadBinaryState(pStream);
      
      return hr;
  }
  
  //=--------------------------------------------------------------------------=
  // COleControl::Save    [IPersistStreamInit]
  //=--------------------------------------------------------------------------=
  // saves out our state using streams
  //
  // Parameters:
  //    IStream *        - [in]
  //    BOOL             - [in] clear dirty bit?
  //
  // Output:
  //    HRESULT
  //
  // Notes:
  //
  STDMETHODIMP COleControl::Save
  (
      IStream *pStream,
      BOOL     fClearDirty
  )
  {
      HRESULT hr;
  
      // use our helper routine that we share with the IStorage persistence
      // code.
      //
      hr = m_SaveToStream(pStream);
      RETURN_ON_FAILURE(hr);
  
      // clear out dirty flag [if appropriate] and notify that we're done
      // with save.
      //
      if (fClearDirty)
          m_fDirty = FALSE;
      if (m_pOleAdviseHolder)
          m_pOleAdviseHolder->SendOnSave();
  
      return S_OK;
  }
  
  //=--------------------------------------------------------------------------=
  // COleControl::InitNew    [IPersistStorage]
  //=--------------------------------------------------------------------------=
  // ipersiststorage version of this.  fweee
  //
  // Parameters:
  //    IStorage *    - [in] we don't use this
  //
  // Output:
  //    HRESULT
  //
  // Notes:
  //
  STDMETHODIMP COleControl::InitNew
  (
      IStorage *pStorage
  )
  {
      // we already have an implementation of this [for IPersistStreamInit]
      //
      return InitNew();
  }
  
  //=--------------------------------------------------------------------------=
  // COleControl::Load    [IPersistStorage]
  //=--------------------------------------------------------------------------=
  // Ipersiststorage version of this
  //
  // Parameters:
  //    IStorage *    - [in] DUH.
  //
  // Output:
  //    HRESULT
  //
  // Notes:
  //
  STDMETHODIMP COleControl::Load(IStorage *pStorage)
  {
      IStream *pStream;
      HRESULT  hr;
  
      // we're going to use IPersistStream::Load from the CONTENTS stream.
      //
      hr = pStorage->OpenStream(wszCtlSaveStream, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
      RETURN_ON_FAILURE(hr);
  
      // IPersistStreamInit::Load
      //
      hr = Load(pStream);
      pStream->Release();
      return hr;
  }
  
  //=--------------------------------------------------------------------------=
  // COleControl::Save    [IPersistStorage]
  //=--------------------------------------------------------------------------=
  // save into the contents stream of the given storage object.
  //
  // Parameters:
  //    IStorage *        - [in] 10 points if you figure it out
  //    BOOL              - [in] is the storage the same as the load storage?
  //
  // Output:
  //    HRESULT
  //
  // Notes:
  //
  STDMETHODIMP COleControl::Save
  (
      IStorage *pStorage,
      BOOL      fSameAsLoad
  )
  {
      IStream *pStream;
      HRESULT  hr;
  
      // we're just going to save out to the CONTENTES stream.
      //
      hr = pStorage->CreateStream(wszCtlSaveStream, STGM_WRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE,
                                  0, 0, &pStream);
      RETURN_ON_FAILURE(hr);
  
      // use our helper routine.
      //
      hr = m_SaveToStream(pStream);
      m_fSaveSucceeded = (FAILED(hr)) ? FALSE : TRUE;
      pStream->Release();
      return hr;
  }
  
  //=--------------------------------------------------------------------------=
  // COleControl::SaveCompleted    [IPersistStorage]
  //=--------------------------------------------------------------------------=
  // lets us clear out our flags.
  //
  // Parameters:
  //    IStorage *    - ignored
  //
  // Output:
  //    HRESULT
  //
  // Notes:
  //
  STDMETHODIMP COleControl::SaveCompleted
  (
      IStorage *pStorageNew
  )
  {
      // if our save succeeded, then we can do our post save work.
      //
      if (m_fSaveSucceeded) {
          m_fDirty = FALSE;
          if (m_pOleAdviseHolder)
              m_pOleAdviseHolder->SendOnSave();
      }
  
      return S_OK;
  }
  
  //=--------------------------------------------------------------------------=
  // COleControl::HandsOffStorage    [IPersistStorage]
  //=--------------------------------------------------------------------------=
  // not interesting
  //
  // Output:
  //    S_OK
  //
  // Notes:
  //
  STDMETHODIMP COleControl::HandsOffStorage
  (
      void
  )
  {
      // we don't ever hold on to  a storage pointer, so this is remarkably
      // uninteresting to us.
      //
      return S_OK;
  }
  
  //=--------------------------------------------------------------------------=
  // COleControl::m_SaveToStream    [helper: IPersistStreamInit/IPersistStorage]
  //=--------------------------------------------------------------------------=
  // save ourselves to a stream
  //
  // Parameters:
  //    IStream *        - figure it out
  //
  // Output:
  //    HRESULT
  //
  // Notes:
  //
  HRESULT COleControl::m_SaveToStream
  (
      IStream *pStream
  )
  {
      HRESULT hr;
  
      // save out standard state information that the user has no control
      // over
      //
      hr = SaveStandardState(pStream);
      RETURN_ON_FAILURE(hr);
  
      // save out user-specific satte information.  they MUST implement this
      // function
      //
      hr = SaveBinaryState(pStream);
  
      return hr;
  }
  
  //=--------------------------------------------------------------------------=
  // COleControl::LoadStandardState    [ helper ]
  //=--------------------------------------------------------------------------=
  // reads in standard properties that all controls are going to have, using
  // text persistence APIs.  there is another version for streams.
  //
  // Parameters:
  //    IPropertyBag *    - [in]
  //    IErrorLog *       - [in]
  //
  // Output:
  //    HRESULT
  //
  // Notes:
  //
  HRESULT COleControl::LoadStandardState
  (
      IPropertyBag *pPropertyBag,
      IErrorLog    *pErrorLog
  )
  {
      VARIANT v;
      HRESULT hr;
      SIZEL   slHiMetric = { 100, 50 };
  
      // currently, our only standard properties are related to size.
      // if we can't find them, then we'll just use some defaults.
      //
      v.vt = VT_I4;
      v.lVal = 0;
      hr = pPropertyBag->Read(L"_ExtentX", &v, pErrorLog);
      if (SUCCEEDED(hr)) slHiMetric.cx = v.lVal;
  
      v.lVal = 0;
      hr = pPropertyBag->Read(L"_ExtentY", &v, pErrorLog);
      if (SUCCEEDED(hr)) slHiMetric.cy = v.lVal;
  
      HiMetricToPixel(&slHiMetric, &m_Size);
      return S_OK;
  }
  
  //=--------------------------------------------------------------------------=
  // COleControl::LoadStandardState    [ helper ]
  //=--------------------------------------------------------------------------=
  // reads in standard properties that all controls are going to have, using
  // stream persistence APIs.  there is another version for text.
  //
  // Parameters:
  //    IStream *         - [in] 
  //
  // Output:
  //    HRESULT
  //
  // Notes:
  //
  HRESULT COleControl::LoadStandardState
  (
      IStream *pStream
  )
  {
      STREAMHDR stmhdr;
      HRESULT hr;
      SIZEL   slHiMetric;
  
      // look for our header structure, so we can verify stream validity.
      //
      hr = pStream->Read(&stmhdr, sizeof(STREAMHDR), NULL);
      RETURN_ON_FAILURE(hr);
  
      if (stmhdr.dwSignature != STREAMHDR_SIGNATURE)
          return E_UNEXPECTED;
  
      // currently, the only standard state we're writing out is
      // a SIZEL structure describing the control's size.
      //
      if (stmhdr.cbWritten != sizeof(m_Size))
          return E_UNEXPECTED;
  
      // we like the stream.  let's go load in our two properties.
      //
      hr = pStream->Read(&slHiMetric, sizeof(slHiMetric), NULL);
      RETURN_ON_FAILURE(hr);
  
      HiMetricToPixel(&slHiMetric, &m_Size);
      return S_OK;
  }
  
  //=--------------------------------------------------------------------------=
  // COleControl::SaveStandardState    [ helper ]
  //=--------------------------------------------------------------------------=
  // saves out standard properties that we're managing for a control using text
  // persistence APIs.  there is another version for stream persistence.
  //
  // Parameters:
  //    IPropertyBag *        - [in]
  //
  // Output:
  //    HRESULT
  //
  // Notes:
  //
  HRESULT COleControl::SaveStandardState
  (
      IPropertyBag *pPropertyBag
  )
  {
      HRESULT hr;
      VARIANT v;
      SIZEL   slHiMetric;
  
      // currently, the only standard proprerties we persist are Size related
      //
      PixelToHiMetric(&m_Size, &slHiMetric);
  
      v.vt = VT_I4;
      v.lVal = slHiMetric.cx;
  
      hr = pPropertyBag->Write(L"_ExtentX", &v);
      RETURN_ON_FAILURE(hr);
  
      v.lVal = slHiMetric.cy;
  
      hr = pPropertyBag->Write(L"_ExtentY", &v);
  
      return hr;
  }
  
  //=--------------------------------------------------------------------------=
  // COleControl::SaveStandardState    [ helper ]
  //=--------------------------------------------------------------------------=
  // saves out standard properties that we're managing for a control using stream
  // persistence APIs.  there is another version for text persistence.
  //
  // Parameters:
  //    IStream *            - [in]
  //
  // Output:
  //    HRESULT
  //
  // Notes:
  //
  HRESULT COleControl::SaveStandardState
  (
      IStream *pStream
  )
  {
      STREAMHDR streamhdr = { STREAMHDR_SIGNATURE, sizeof(SIZEL) };
      HRESULT hr;
      SIZEL   slHiMetric;
  
  
      // first thing to do is write out our stream hdr structure.
      //
      hr = pStream->Write(&streamhdr, sizeof(STREAMHDR), NULL);
      RETURN_ON_FAILURE(hr);
  
      // the only properties we're currently persisting here are the size
      // properties for this control.  make sure we do that in HiMetric
      //
      PixelToHiMetric(&m_Size, &slHiMetric);
  
      hr = pStream->Write(&slHiMetric, sizeof(slHiMetric), NULL);
      return hr;
  }
  
  //=--------------------------------------------------------------------------=
  // COleControl::InitializeNewState    [overridable]
  //=--------------------------------------------------------------------------=
  // the user can override this to initialize variables
  //
  // Output:
  //    BOOL        - FALSE means couldn't do it.
  //
  // Notes:
  //
  BOOL COleControl::InitializeNewState
  (
      void
  )
  {
      // we find this largely uninteresting
      //
      return TRUE;
  }
  
  
  
  