home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / com / tutsamp / perserve / pagelist.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-05  |  56.3 KB  |  1,705 lines

  1. /*+==========================================================================
  2.   File:      PAGELIST.CPP
  3.  
  4.   Summary:   Implementation file for the COPageList COM Object Class (for
  5.              connectable COPageList COM Objects). COPageList encapsulates
  6.              into a persistent COM object the management of a page list.
  7.              The list holds items that represent pages of user-editable
  8.              information. Currently, two page types are supported in this
  9.              sample: Text pages and Drawing pages. The client can use the
  10.              custom IPageList interface to perform such operations on the
  11.              list as: Get a list item, Set a list item, Add an item to the
  12.              list, Delete an item from the list, and Clear the list.
  13.              COPageList objects expose the IPersistStream interface and
  14.              thus support its method of stream persistence for the list
  15.              data. This persistence support is covered in detail in
  16.              PERSERVE.HTM.
  17.  
  18.              Connectable object technology is used in COPageList to notify
  19.              connected clients of various events like when a page item is
  20.              added to the list.
  21.  
  22.              COPageList offers a main standard IUnknown interface (basic
  23.              COM object features), the standard IConnectionPointContainer
  24.              interface (connectable COM object features), the standard
  25.              IPersistStream interface (stream persistence features), and
  26.              the custom IPageList interface (PageList related features).
  27.              This multiple interface COM Object Class is achieved via the
  28.              technique of nested classes.  The implementations of the
  29.              various interfaces are nested inside the COPageList Class.
  30.  
  31.              For a comprehensive tutorial code tour of this module's
  32.              contents and offerings see the tutorial PERSERVE.HTM
  33.              file. For more specific technical details on the internal
  34.              workings see the comments dispersed throughout the module's
  35.              source code.
  36.  
  37.   Classes:   COPageList.
  38.  
  39.   Functions: none.
  40.  
  41.   Origin:    2-5-97: atrent - Editor-inheritance from PAPER.CPP in
  42.              the STOSERVE Tutorial Code Sample.
  43.  
  44. ----------------------------------------------------------------------------
  45.   This file is part of the Microsoft COM Tutorial Code Samples.
  46.  
  47.   Copyright (C) Microsoft Corporation, 1997.  All rights reserved.
  48.  
  49.   This source code is intended only as a supplement to Microsoft
  50.   Development Tools and/or on-line documentation.  See these other
  51.   materials for detailed information regarding Microsoft code samples.
  52.  
  53.   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  54.   KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  55.   IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  56.   PARTICULAR PURPOSE.
  57. ==========================================================================+*/
  58.  
  59.  
  60. /*---------------------------------------------------------------------------
  61.   We include WINDOWS.H for all Win32 applications.
  62.   We include OLE2.H because we will be calling the COM/OLE Libraries.
  63.   We include OLECTL.H because it has definitions for connectable objects.
  64.   We include APPUTIL.H because we will be building this application using
  65.     the convenient Virtual Window and Dialog classes and other
  66.     utility functions in the APPUTIL Library (ie, APPUTIL.LIB).
  67.   We include IPAGES.H and PAGEGUID.H for the common PageList-related
  68.     Interface class, GUID, and CLSID specifications.
  69.   We include SERVER.H because it has internal class declarations for
  70.     the server's control object.
  71.   We include CONNECT.H for object class declarations for the various
  72.     connection point and connection COM objects used in PERSERVE.
  73.   We include PAGELIST.H because it has the COPageList class declarations.
  74. ---------------------------------------------------------------------------*/
  75. #include <windows.h>
  76. #include <ole2.h>
  77. #include <olectl.h>
  78. #include <apputil.h>
  79. #include <ipages.h>
  80. #include <pageguid.h>
  81. #include "server.h"
  82. #include "connect.h"
  83. #include "pagelist.h"
  84.  
  85.  
  86. /*---------------------------------------------------------------------------
  87.   COPageList's implementation of its main COM object class including
  88.   Constructor, Destructor, QueryInterface, AddRef, and Release.
  89. ---------------------------------------------------------------------------*/
  90.  
  91. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  92.   Method:   COPageList::COPageList
  93.  
  94.   Summary:  COPageList Constructor. Note the member initializer:
  95.             "m_ImpIPageList(this, pUnkOuter)" which is used to pass the
  96.             'this' and pUnkOuter pointers of the constructor function to
  97.             the constructor in the instantiation of the implementation of
  98.             the CImpIPageList interface (which is nested inside this
  99.             present COPageList Object Class). Same technique is used for
  100.             the other nested interface implementations.
  101.  
  102.   Args:     IUnknown* pUnkOuter,
  103.               Pointer to the the outer Unknown.  NULL means this COM Object
  104.               is not being Aggregated.  Non NULL means it is being created
  105.               on behalf of an outside COM object that is reusing it via
  106.               aggregation.
  107.             CServer* pServer)
  108.               Pointer to the server's control object.
  109.  
  110.   Modifies: m_cRefs, m_pUnkOuter, m_pServer.
  111.  
  112.   Returns:  void
  113. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  114. COPageList::COPageList(
  115.   IUnknown* pUnkOuter,
  116.   CServer* pServer) :
  117.   m_ImpIPageList(this, pUnkOuter),
  118.   m_ImpIPersistStream(this, pUnkOuter),
  119.   m_ImpIConnectionPointContainer(this, pUnkOuter)
  120. {
  121.   UINT i;
  122.  
  123.   // Zero the COM object's reference count.
  124.   m_cRefs = 0;
  125.  
  126.   // No AddRef necessary if non-NULL, as we're nested.
  127.   m_pUnkOuter = pUnkOuter;
  128.  
  129.   // Assign the pointer to the server control object.
  130.   m_pServer = pServer;
  131.  
  132.   // Null all entries in the connection point array.
  133.   for (i=0; i<MAX_CONNECTION_POINTS; i++)
  134.     m_aConnectionPoints[i] = NULL;
  135.  
  136.   // Now initialize the PageList itself.
  137.   m_lPageListEnd = 0;
  138.   m_lPageListMax = 0;
  139.   m_paPageList   = NULL;
  140.   m_bDirty       = TRUE;
  141.   m_ClassID      = CLSID_PageList;
  142.  
  143.   return;
  144. }
  145.  
  146.  
  147. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  148.   Method:   COPageList::~COPageList
  149.  
  150.   Summary:  COPageList Destructor.
  151.  
  152.   Args:     void
  153.  
  154.   Returns:  void
  155. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  156. COPageList::~COPageList(void)
  157. {
  158.   UINT i;
  159.   IConnectionPoint* pIConnectionPoint;
  160.   PAGEITEM* paPageList;
  161.  
  162.   // Do final release of the connection point objects.
  163.   // If this isn't the final release, then the client has an outstanding
  164.   // unbalanced reference to a connection point and a memory leak may
  165.   // likely result because the host COPageList object is now going away yet
  166.   // a connection point for this host object will not end up deleting
  167.   // itself (and its connections array).
  168.   for (i=0; i<MAX_CONNECTION_POINTS; i++)
  169.   {
  170.     pIConnectionPoint = m_aConnectionPoints[i];
  171.     RELEASE_INTERFACE(pIConnectionPoint);
  172.   }
  173.  
  174.   // NULL the pointer first and then delete the entire PageList array.
  175.   paPageList = m_paPageList;
  176.   m_paPageList = NULL;
  177.   if (NULL != paPageList)
  178.     delete [] paPageList;
  179.  
  180.   return;
  181. }
  182.  
  183.  
  184. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  185.   Method:   COPageList::Init
  186.  
  187.   Summary:  COPageList initialization method.  Create any necessary
  188.             arrays, structures, and subordinate objects. In this case the
  189.             COM object is initialized as a connectable object.
  190.  
  191.   Args:     void
  192.  
  193.   Modifies: m_aConnectionPoints.
  194.  
  195.   Returns:  HRESULT
  196.               Standard result code. NOERROR for success.
  197. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  198. HRESULT COPageList::Init(void)
  199. {
  200.   HRESULT hr = NOERROR;
  201.   COConnectionPoint* pCOConnPt;
  202.  
  203.   // Rig this COPageList COM object to be connectable. Assign the
  204.   // connection point array. This object's connection points are
  205.   // determined at compile time--it currently has only one connection
  206.   // point: the CONNPOINT_PAGELISTSINK connection point. Create a
  207.   // connection point object for this and assign it into the array. This
  208.   // array could easily grow to support additional connection points in
  209.   // the future.
  210.  
  211.   // First try creating a new connection point object. Pass 'this' as the
  212.   // pHostObj pointer used by the connection point to pass its AddRef and
  213.   // Release calls back to the host connectable object.
  214.   pCOConnPt = new COConnectionPoint(this);
  215.   if (NULL != pCOConnPt)
  216.   {
  217.     // If creation succeeded then initialize it (including creating
  218.     // its initial dynamic connection array).
  219.     hr = pCOConnPt->Init(IID_IPageListSink);
  220.  
  221.     // If the init succeeded then use QueryInterface to obtain the
  222.     // IConnectionPoint interface on the new connection point object.
  223.     // The interface pointer is assigned directly into the
  224.     // connection point array. The QI also does the needed AddRef.
  225.     if (SUCCEEDED(hr))
  226.     {
  227.       hr = pCOConnPt->QueryInterface(
  228.              IID_IConnectionPoint,
  229.              (PPVOID)&m_aConnectionPoints[CONNPOINT_PAGELISTSINK]);
  230.       if (SUCCEEDED(hr))
  231.       {
  232.         // Build the initial empty dynamic array of PageList Items.
  233.         hr = Clear();
  234.  
  235.         // Mark new empty Page List as clean.
  236.         m_bDirty = FALSE;
  237.       }
  238.     }
  239.  
  240.     if (FAILED(hr))
  241.       delete pCOConnPt;
  242.   }
  243.   else
  244.     hr = E_OUTOFMEMORY;
  245.  
  246.   return hr;
  247. }
  248.  
  249.  
  250. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  251.   Method:   COPageList::QueryInterface
  252.  
  253.   Summary:  QueryInterface of the COPageList non-delegating IUnknown
  254.             implementation.
  255.  
  256.   Args:     REFIID riid,
  257.               [in] GUID of the Interface being requested.
  258.             PPVOID ppv)
  259.               [out] Address of the caller's pointer variable that will
  260.               receive the requested interface pointer.
  261.  
  262.   Returns:  HRESULT
  263.               Standard result code. NOERROR for success.
  264. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  265. STDMETHODIMP COPageList::QueryInterface(
  266.                REFIID riid,
  267.                PPVOID ppv)
  268. {
  269.   HRESULT hr = E_NOINTERFACE;
  270.  
  271.   *ppv = NULL;
  272.  
  273.   if (IID_IUnknown == riid)
  274.     *ppv = this;
  275.   else if (IID_IConnectionPointContainer == riid)
  276.     *ppv = &m_ImpIConnectionPointContainer;
  277.   else if (IID_IPersistStream == riid)
  278.     *ppv = &m_ImpIPersistStream;
  279.   else if (IID_IPageList == riid)
  280.     *ppv = &m_ImpIPageList;
  281.  
  282.   if (NULL != *ppv)
  283.   {
  284.     // We've handed out a pointer to the interface so obey the COM rules
  285.     // and AddRef the reference count.
  286.     ((LPUNKNOWN)*ppv)->AddRef();
  287.     hr = NOERROR;
  288.   }
  289.  
  290.   return (hr);
  291. }
  292.  
  293.  
  294. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  295.   Method:   COPageList::AddRef
  296.  
  297.   Summary:  AddRef of the COPageList non-delegating IUnknown implementation.
  298.  
  299.   Args:     void
  300.  
  301.   Returns:  ULONG
  302.               New value of m_cRefs (COM object's reference count).
  303. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  304. STDMETHODIMP_(ULONG) COPageList::AddRef(void)
  305. {
  306.   ULONG cRefs;
  307.  
  308.   cRefs = ++m_cRefs;
  309.  
  310.   return cRefs;
  311. }
  312.  
  313.  
  314. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  315.   Method:   COPageList::Release
  316.  
  317.   Summary:  Release of the COPageList non-delegating IUnknown implementation.
  318.  
  319.   Args:     void
  320.  
  321.   Modifies: m_cRefs.
  322.  
  323.   Returns:  ULONG
  324.               New value of m_cRefs (COM object's reference count).
  325. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  326. STDMETHODIMP_(ULONG) COPageList::Release(void)
  327. {
  328.   ULONG cRefs;
  329.  
  330.   cRefs = --m_cRefs;
  331.  
  332.   if (0 == cRefs)
  333.   {
  334.     // We've reached a zero reference count for this COM object.
  335.     // So we tell the server housing to decrement its global object
  336.     // count so that the server will be unloaded if appropriate.
  337.     if (NULL != m_pServer)
  338.       m_pServer->ObjectsDown();
  339.  
  340.     // We artificially bump the main ref count to prevent reentrancy
  341.     // via the main object destructor.  Not really needed in this
  342.     // COPageList but a good practice because we are aggregatable and
  343.     // may at some point in the future add something entertaining like
  344.     // some Releases to the COPageList destructor. We relinquish thread
  345.     // ownership of this object prior to deleting it--a good practice.
  346.     m_cRefs++;
  347.     delete this;
  348.   }
  349.  
  350.   return cRefs;
  351. }
  352.  
  353.  
  354. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  355.   Method:   COPageList::NotifySinks
  356.  
  357.   Summary:  Internal utility method of this COM object used to fire event
  358.             notification calls to all listening connection sinks in the
  359.             clients.
  360.  
  361.   Args:     PAGELIST_EVENT PageListEvent
  362.               Type of notification event.
  363.  
  364.   Returns:  HRESULT
  365.               Standard result code. NOERROR for success.
  366. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  367. HRESULT COPageList::NotifySinks(
  368.        PAGELIST_EVENT PageListEvent,
  369.        INT iPage)
  370. {
  371.   HRESULT hr = NOERROR;
  372.   IConnectionPoint* pIConnectionPoint;
  373.   IEnumConnections* pIEnum;
  374.   CONNECTDATA ConnData;
  375.  
  376.   // If there was a PageList event, broadcast appropriate notifications to
  377.   // all Sinks connected to each connection point.
  378.   if (PAGELIST_EVENT_NONE != PageListEvent)
  379.   {
  380.     // Here is the section for the PageListSink connection point--currently
  381.     // this is the only connection point offered by COPageList objects.
  382.     pIConnectionPoint = m_aConnectionPoints[CONNPOINT_PAGELISTSINK];
  383.     if (NULL != pIConnectionPoint)
  384.     {
  385.       pIConnectionPoint->AddRef();
  386.       hr = pIConnectionPoint->EnumConnections(&pIEnum);
  387.       if (SUCCEEDED(hr))
  388.       {
  389.         // Loop thru the connection point's connections and if the
  390.         // listening connection supports IPageListSink (ie, PageListSink
  391.         // events) then dispatch the PageListEvent event notification to
  392.         // that sink.
  393.         while (NOERROR == pIEnum->Next(1, &ConnData, NULL))
  394.         {
  395.           IPageListSink* pIPageListSink;
  396.  
  397.           hr = ConnData.pUnk->QueryInterface(
  398.                                 IID_IPageListSink,
  399.                                 (PPVOID)&pIPageListSink);
  400.           if (SUCCEEDED(hr))
  401.           {
  402.             switch (PageListEvent)
  403.             {
  404.               case PAGELIST_EVENT_LOADED:
  405.                 pIPageListSink->Loaded();
  406.                 break;
  407.               case PAGELIST_EVENT_SAVED:
  408.                 pIPageListSink->Saved();
  409.                 break;
  410.               case PAGELIST_EVENT_CLEARED:
  411.                 pIPageListSink->Cleared();
  412.                 break;
  413.               case PAGELIST_EVENT_PAGEADDED:
  414.                 pIPageListSink->PageAdded(iPage);
  415.                 break;
  416.               case PAGELIST_EVENT_PAGEDELETED:
  417.                 pIPageListSink->PageDeleted(iPage);
  418.                 break;
  419.               case PAGELIST_EVENT_PAGESET:
  420.                 pIPageListSink->PageSet(iPage);
  421.                 break;
  422.               default:
  423.                 break;
  424.             }
  425.             pIPageListSink->Release();
  426.           }
  427.           ConnData.pUnk->Release();
  428.         }
  429.         pIEnum->Release();
  430.       }
  431.       pIConnectionPoint->Release();
  432.     }
  433.   }
  434.  
  435.   return hr;
  436. }
  437.  
  438.  
  439. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  440.   Method:   COPageList::Clear
  441.  
  442.   Summary:  Internal utility method of this COM object used to clear
  443.             the PageList array.
  444.  
  445.   Args:     none.
  446.  
  447.   Returns:  HRESULT
  448.               Standard result code. NOERROR for success.
  449. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  450. HRESULT COPageList::Clear(
  451.           void)
  452. {
  453.   HRESULT hr = E_FAIL;
  454.   PAGEITEM* paPageList;
  455.  
  456.   // Allocate new array space for a fresh empty Page List array.
  457.   paPageList = new PAGEITEM[(LONG)(PAGELIST_ALLOC_INIT)];
  458.   if (NULL != paPageList)
  459.   {
  460.     // Zero the array.
  461.     memset(paPageList, 0, PAGELIST_ALLOC_INIT * sizeof(PAGEITEM));
  462.  
  463.     // New array is ready--delete the old array.
  464.     if (NULL != m_paPageList)
  465.       delete [] m_paPageList;
  466.  
  467.     // Rig the PageList to use the new array.
  468.     m_paPageList = paPageList;
  469.     m_lPageListMax = PAGELIST_ALLOC_INIT;
  470.     m_lPageListEnd = 0;
  471.  
  472.     // Zero the PageList Properties structure. Init the PageList version.
  473.     memset(&m_PageListProps, 0, sizeof(PAGELISTPROPS));
  474.     m_PageListProps.lPageListVersion = PAGELIST_VERSION10;
  475.  
  476.     // Mark PageList as dirty.
  477.     m_bDirty = TRUE;
  478.     hr = NOERROR;
  479.   }
  480.   else
  481.     hr = E_OUTOFMEMORY;
  482.  
  483.   return hr;
  484. }
  485.  
  486.  
  487. /*---------------------------------------------------------------------------
  488.   COPageList's nested implementation of the COM standard
  489.   IConnectionPointContainer interface including Constructor, Destructor,
  490.   QueryInterface, AddRef, Release, FindConnectionPoint, and
  491.   EnumConnectionPoints.
  492. ---------------------------------------------------------------------------*/
  493.  
  494. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  495.   Method:   COPageList::CImpIConnectionPointContainer
  496.               ::CImpIConnectionPointContainer
  497.  
  498.   Summary:  Constructor for the CImpIConnectionPointContainer interface
  499.             instantiation.
  500.  
  501.   Args:     COPageList* pCO,
  502.               Back pointer to the parent outer object.
  503.             IUnknown* pUnkOuter
  504.               Pointer to the outer Unknown.  For delegation.
  505.  
  506.   Modifies: m_pCO, m_pUnkOuter.
  507.  
  508.   Returns:  void
  509. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  510. COPageList::CImpIConnectionPointContainer::CImpIConnectionPointContainer(
  511.   COPageList* pCO,
  512.   IUnknown* pUnkOuter)
  513. {
  514.   // Init the Main Object Pointer to point to the parent object.
  515.   m_pCO = pCO;
  516.  
  517.   // Init the CImpIConnectionPointContainer interface's delegating Unknown
  518.   // pointer.  We use the Main Object pointer for IUnknown delegation here
  519.   // if we are not being aggregated.  If we are being aggregated we use
  520.   // the supplied pUnkOuter for IUnknown delegation.  In either case the
  521.   // pointer assignment requires no AddRef because the
  522.   // CImpIConnectionPointContainer lifetime is quaranteed by the lifetime
  523.   // of the parent object in which CImpIConnectionPointContainer is
  524.   // nested.
  525.   if (NULL == pUnkOuter)
  526.     m_pUnkOuter = pCO;
  527.   else
  528.     m_pUnkOuter = pUnkOuter;
  529.  
  530.   return;
  531. }
  532.  
  533.  
  534. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  535.   Method:   COPageList::CImpIConnectionPointContainer
  536.               ::~CImpIConnectionPointContainer
  537.  
  538.   Summary:  Destructor for the CImpIConnectionPointContainer interface
  539.             instantiation.
  540.  
  541.   Args:     void
  542.  
  543.   Returns:  void
  544. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  545. COPageList::CImpIConnectionPointContainer::~CImpIConnectionPointContainer(void)
  546. {
  547.   return;
  548. }
  549.  
  550.  
  551. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  552.   Method:   COPageList::CImpIConnectionPointContainer::QueryInterface
  553.  
  554.   Summary:  The QueryInterface IUnknown member of this interface
  555.             implementation that delegates to m_pUnkOuter, whatever it is.
  556.  
  557.   Args:     REFIID riid,
  558.               [in] GUID of the Interface being requested.
  559.             PPVOID ppv)
  560.               [out] Address of the caller's pointer variable that will
  561.               receive the requested interface pointer.
  562.  
  563.   Returns:  HRESULT
  564.               Standard result code. NOERROR for success.
  565.               Returned by the delegated outer QueryInterface call.
  566. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  567. STDMETHODIMP COPageList::CImpIConnectionPointContainer::QueryInterface(
  568.                REFIID riid,
  569.                PPVOID ppv)
  570. {
  571.   // Delegate this call to the outer object's QueryInterface.
  572.   return m_pUnkOuter->QueryInterface(riid, ppv);
  573. }
  574.  
  575.  
  576. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  577.   Method:   COPageList::CImpIConnectionPointContainer::AddRef
  578.  
  579.   Summary:  The AddRef IUnknown member of this IPageList interface
  580.             implementation that delegates to m_pUnkOuter, whatever it is.
  581.  
  582.   Args:     void
  583.  
  584.   Returns:  ULONG
  585.               Returned by the delegated outer AddRef call.
  586. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  587. STDMETHODIMP_(ULONG) COPageList::CImpIConnectionPointContainer::AddRef(void)
  588. {
  589.   // Delegate this call to the outer object's AddRef.
  590.   return m_pUnkOuter->AddRef();
  591. }
  592.  
  593.  
  594. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  595.   Method:   COPageList::CImpIConnectionPointContainer::Release
  596.  
  597.   Summary:  The Release IUnknown member of this interface implementation
  598.             that delegates to m_pUnkOuter, whatever it is.
  599.  
  600.   Args:     void
  601.  
  602.   Returns:  ULONG
  603.               Returned by the delegated outer Release call.
  604. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  605. STDMETHODIMP_(ULONG) COPageList::CImpIConnectionPointContainer::Release(void)
  606. {
  607.   // Delegate this call to the outer object's Release.
  608.   return m_pUnkOuter->Release();
  609. }
  610.  
  611.  
  612. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  613.   Method:   COPageList::CImpIConnectionPointContainer::FindConnectionPoint
  614.  
  615.   Summary:  Given an IID for a connection point sink find and return the
  616.             interface pointer for that connection point sink.
  617.  
  618.   Args:     REFIID riid
  619.               Reference to an IID
  620.             IConnectionPoint** ppConnPt
  621.               Address of the caller's IConnectionPoint interface pointer
  622.               variable that will receive the requested interface pointer.
  623.  
  624.   Returns:  HRESULT
  625.               Standard result code. NOERROR for success.
  626. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  627. STDMETHODIMP COPageList::CImpIConnectionPointContainer::FindConnectionPoint(
  628.                REFIID riid,
  629.                IConnectionPoint** ppConnPt)
  630. {
  631.   HRESULT hr = E_NOINTERFACE;
  632.   IConnectionPoint* pIConnPt;
  633.  
  634.   // NULL the output variable.
  635.   *ppConnPt = NULL;
  636.  
  637.   pIConnPt = m_pCO->m_aConnectionPoints[CONNPOINT_PAGELISTSINK];
  638.   if (NULL != pIConnPt)
  639.   {
  640.     // This connectable COPageList object currently has only the
  641.     // PageList Sink connection point. If the associated interface is
  642.     // requested, use QI to get the Connection Point interface and
  643.     // perform the needed AddRef.
  644.     if (IID_IPageListSink == riid)
  645.       hr = pIConnPt->QueryInterface(
  646.                        IID_IConnectionPoint,
  647.                        (PPVOID)ppConnPt);
  648.   }
  649.  
  650.   return hr;
  651. }
  652.  
  653.  
  654. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  655.   Method:   COPageList::CImpIConnectionPointContainer::EnumConnectionPoints
  656.  
  657.   Summary:  Return Enumerator for the connectable object's contained
  658.             connection points.
  659.  
  660.   Args:     IEnumConnectionPoints** ppIEnum
  661.               Address of the caller's Enumerator interface pointer
  662.               variable. An output variable that will receive a pointer to
  663.               the connection point enumerator COM object.
  664.  
  665.   Returns:  HRESULT
  666.               Standard result code. NOERROR for success.
  667. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  668. STDMETHODIMP COPageList::CImpIConnectionPointContainer::EnumConnectionPoints(
  669.                IEnumConnectionPoints** ppIEnum)
  670. {
  671.   HRESULT hr = NOERROR;
  672.   IConnectionPoint* aConnPts[MAX_CONNECTION_POINTS];
  673.   COEnumConnectionPoints* pCOEnum;
  674.   UINT i;
  675.  
  676.   // Zero the output interface pointer.
  677.   *ppIEnum = NULL;
  678.  
  679.   // Make a copy on the stack of the array of connection point
  680.   // interfaces. The copy is used below in the creation of the new
  681.   // Enumerator object.
  682.   for (i=0; i<MAX_CONNECTION_POINTS; i++)
  683.     aConnPts[i] = (IConnectionPoint*)m_pCO->m_aConnectionPoints[i];
  684.  
  685.   // Create a Connection Point enumerator COM object for the connection
  686.   // points offered by this COPageList object. Pass 'this' to be used to
  687.   // hook the lifetime of the host object to the life time of this
  688.   // enumerator object.
  689.   pCOEnum = new COEnumConnectionPoints(this);
  690.   if (NULL != pCOEnum)
  691.   {
  692.     // Use the array copy to Init the new Enumerator COM object.
  693.     // Set the initial Enumerator index to 0.
  694.     hr = pCOEnum->Init(MAX_CONNECTION_POINTS, aConnPts, 0);
  695.     if (SUCCEEDED(hr))
  696.     {
  697.       // QueryInterface to return the requested interface pointer.
  698.       // An AddRef will be conveniently done by the QI.
  699.       if (SUCCEEDED(hr))
  700.         hr = pCOEnum->QueryInterface(
  701.                         IID_IEnumConnectionPoints,
  702.                         (PPVOID)ppIEnum);
  703.     }
  704.   }
  705.   else
  706.     hr = E_OUTOFMEMORY;
  707.  
  708.   return hr;
  709. }
  710.  
  711.  
  712. /*---------------------------------------------------------------------------
  713.   COPageList's nested implementation of the COM standard
  714.   IPersistStream interface including Constructor, Destructor,
  715.   QueryInterface, AddRef, Release, GetClassID, IsDirty, Load, Save,
  716.   and GetSizeMax.
  717. ---------------------------------------------------------------------------*/
  718.  
  719. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  720.   Method:   COPageList::CImpIPersistStream::CImpIPersistStream
  721.  
  722.   Summary:  Constructor for the CImpIPersistStream interface
  723.             instantiation.
  724.  
  725.   Args:     COPageList* pCO,
  726.               Back pointer to the parent outer object.
  727.             IUnknown* pUnkOuter
  728.               Pointer to the outer Unknown.  For delegation.
  729.  
  730.   Modifies: m_pCO, m_pUnkOuter.
  731.  
  732.   Returns:  void
  733. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  734. COPageList::CImpIPersistStream::CImpIPersistStream(
  735.   COPageList* pCO,
  736.   IUnknown* pUnkOuter)
  737. {
  738.   // Init the Back Object Pointer to point to the parent object.
  739.   m_pCO = pCO;
  740.  
  741.   // Init the CImpIPersistStream interface's delegating Unknown
  742.   // pointer.  We use the Main Object pointer for IUnknown delegation here
  743.   // if we are not being aggregated.  If we are being aggregated we use
  744.   // the supplied pUnkOuter for IUnknown delegation.  In either case the
  745.   // pointer assignment requires no AddRef because the
  746.   // pIPersistStream lifetime is quaranteed by the lifetime of the
  747.   // parent object in which CImpIPersistStream is nested.
  748.   if (NULL == pUnkOuter)
  749.     m_pUnkOuter = pCO;
  750.   else
  751.     m_pUnkOuter = pUnkOuter;
  752.  
  753.   return;
  754. }
  755.  
  756.  
  757. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  758.   Method:   COPageList::CImpIPersistStream::~CImpIPersistStream
  759.  
  760.   Summary:  Destructor for the CImpIPersistStreamIint interface
  761.             instantiation.
  762.  
  763.   Args:     void
  764.  
  765.   Returns:  void
  766. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  767. COPageList::CImpIPersistStream::~CImpIPersistStream(void)
  768. {
  769.   return;
  770. }
  771.  
  772.  
  773. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  774.   Method:   COPageList::CImpIPersistStream::QueryInterface
  775.  
  776.   Summary:  The QueryInterface IUnknown member of this interface
  777.             implementation that delegates to m_pUnkOuter, whatever it is.
  778.  
  779.   Args:     REFIID riid,
  780.               [in] GUID of the Interface being requested.
  781.             PPVOID ppv)
  782.               [out] Address of the caller's pointer variable that will
  783.               receive the requested interface pointer.
  784.  
  785.   Returns:  HRESULT
  786.               Standard result code. NOERROR for success.
  787.               Returned by the delegated outer QueryInterface call.
  788. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  789. STDMETHODIMP COPageList::CImpIPersistStream::QueryInterface(
  790.                REFIID riid,
  791.                PPVOID ppv)
  792. {
  793.   // Delegate this call to the outer object's QueryInterface.
  794.   return m_pUnkOuter->QueryInterface(riid, ppv);
  795. }
  796.  
  797.  
  798. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  799.   Method:   COPageList::CImpIPersistStream::AddRef
  800.  
  801.   Summary:  The AddRef IUnknown member of this interface implementation
  802.             that delegates to m_pUnkOuter, whatever it is.
  803.  
  804.   Args:     void
  805.  
  806.   Returns:  ULONG
  807.               Returned by the delegated outer AddRef call.
  808. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  809. STDMETHODIMP_(ULONG) COPageList::CImpIPersistStream::AddRef(void)
  810. {
  811.   // Delegate this call to the outer object's AddRef.
  812.   return m_pUnkOuter->AddRef();
  813. }
  814.  
  815.  
  816. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  817.   Method:   COPageList::CImpIPersistStream::Release
  818.  
  819.   Summary:  The Release IUnknown member of this interface implementation
  820.             that delegates to m_pUnkOuter, whatever it is.
  821.  
  822.   Args:     void
  823.  
  824.   Returns:  ULONG
  825.               Returned by the delegated outer Release call.
  826. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  827. STDMETHODIMP_(ULONG) COPageList::CImpIPersistStream::Release(void)
  828. {
  829.   // Delegate this call to the outer object's Release.
  830.   return m_pUnkOuter->Release();
  831. }
  832.  
  833.  
  834. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  835.   Method:   COPageList::CImpIPersistStream::GetClassID
  836.  
  837.   Summary:  A method inherited from IPersist. Get the Class ID of this
  838.             COM object.
  839.  
  840.   Args:     CLSID* pClassID
  841.               [out] Address of caller's CLSID variable to hold Class ID.
  842.  
  843.   Returns:  HRESULT
  844.               Standard result code. NOERROR for success.
  845. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  846. STDMETHODIMP COPageList::CImpIPersistStream::GetClassID(
  847.                CLSID* pClassID)
  848. {
  849.   HRESULT hr = E_POINTER;
  850.  
  851.   if (NULL != pClassID)
  852.   {
  853.     // Use overloaded '=' operator to copy the Class ID.
  854.     *pClassID = m_pCO->m_ClassID;
  855.     hr = NOERROR;
  856.   }
  857.  
  858.   return hr;
  859. }
  860.  
  861.  
  862. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  863.   Method:   COPageList::CImpIPersistStream::IsDirty
  864.  
  865.   Summary:  Called to determine if changes were made to this COM object's
  866.             data since it was last loaded, initialized, or saved.
  867.  
  868.   Args:     none.
  869.  
  870.   Returns:  HRESULT
  871.               Standard result code. S_OK if dirty; S_FALSE if not.
  872. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  873. STDMETHODIMP COPageList::CImpIPersistStream::IsDirty(
  874.                void)
  875. {
  876.   HRESULT hr = E_FAIL;
  877.  
  878.   hr = m_pCO->m_bDirty ? S_OK : S_FALSE;
  879.  
  880.   return hr;
  881. }
  882.  
  883.  
  884. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  885.   Method:   COPageList::CImpIPersistStream::Load
  886.  
  887.   Summary:  Asks this COM object to load its persistent data from the
  888.             specified stream at the current seek pointer. This function
  889.             assumes the seek pointer is the same as it was before Save
  890.             was last called. This function must leave the seek pointer
  891.             the same as it was when Save was last completed regardless
  892.             of success or failure. This function should not store a copy
  893.             of pIStream in the COM object and should release it when done.
  894.  
  895.             Load is called when this COM object already has a persistent
  896.             state stored in a stream. Notifies all other connected clients
  897.             when the load is complete.
  898.  
  899.   Args:     IStream* pIStream
  900.               IStream interface pointer for stream to load from.
  901.  
  902.   Returns:  HRESULT
  903.               Standard result code. NOERROR for success.
  904. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  905. STDMETHODIMP COPageList::CImpIPersistStream::Load(
  906.                IStream* pIStream)
  907. {
  908.   HRESULT hr = E_POINTER;
  909.   PAGEITEM* paPageList;
  910.   ULONG ulToRead, ulReadIn;
  911.   LONG lNewArraySize;
  912.   PAGELISTPROPS NewProps;
  913.  
  914.   if (NULL != pIStream)
  915.   {
  916.     // We have the PageList data stream. First read the
  917.     // PageList Properties.
  918.     ulToRead = sizeof(PAGELISTPROPS);
  919.     hr = pIStream->Read(
  920.                      &NewProps,
  921.                      ulToRead,
  922.                      &ulReadIn);
  923.     if (SUCCEEDED(hr) && ulToRead != ulReadIn)
  924.       hr = E_FAIL;
  925.     if (SUCCEEDED(hr))
  926.     {
  927.       // Deal with the different PageList versions.
  928.       switch (NewProps.lPageListVersion)
  929.       {
  930.         case PAGELIST_VERSION10:
  931.           // Allocate a page list array big enough--add some extra
  932.           // for later expansion by user.
  933.           lNewArraySize = NewProps.lPageListSize + PAGELIST_ALLOC;
  934.           paPageList = new PAGEITEM[(LONG) lNewArraySize];
  935.           if (NULL != paPageList)
  936.           {
  937.             // Delete the entire old Page List array.
  938.             if (NULL != m_pCO->m_paPageList)
  939.               delete [] m_pCO->m_paPageList;
  940.  
  941.             // Assign the new array.
  942.             m_pCO->m_paPageList = paPageList;
  943.             m_pCO->m_lPageListMax = lNewArraySize;
  944.  
  945.             // Now read the complete array of Page List items.
  946.             ulToRead = NewProps.lPageListSize * sizeof(PAGEITEM);
  947.             hr = pIStream->Read(m_pCO->m_paPageList, ulToRead, &ulReadIn);
  948.             if (SUCCEEDED(hr) && ulToRead != ulReadIn)
  949.               hr = E_FAIL;
  950.             if (SUCCEEDED(hr))
  951.             {
  952.               // Rig COPageList to use the PAGELISTPROPS info.
  953.               m_pCO->m_lPageListEnd = NewProps.lPageListSize - 1;
  954.  
  955.               // Copy the new properties into current properties.
  956.               memcpy(
  957.                 &m_pCO->m_PageListProps,
  958.                 &NewProps,
  959.                 sizeof(PAGELISTPROPS));
  960.             }
  961.           }
  962.           else
  963.             hr = E_OUTOFMEMORY;
  964.           break;
  965.         default:
  966.           hr = E_FAIL;  // Bad version.
  967.           break;
  968.       }
  969.     }
  970.   }
  971.  
  972.   // Notify all other connected clients that PageList is now loaded.
  973.   // If we didn't load then clear to a safe, empty Page List array.
  974.   if (SUCCEEDED(hr))
  975.     m_pCO->NotifySinks(PAGELIST_EVENT_LOADED,0);
  976.   else
  977.     m_pCO->Clear();
  978.  
  979.   return hr;
  980. }
  981.  
  982.  
  983. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  984.   Method:   COPageList::CImpIPersistStream::Save
  985.  
  986.   Summary:  Called to save the persistent data of this COM object to a
  987.             stream using the specified IStream interface. This call stores
  988.             data from the current seek pointer offset. On exit from this
  989.             method the seek pointer is assumed to be at the end of the
  990.             data saved. This allows a series of contiguous persistent
  991.             objects to be saved into the same stream. Notifies all other
  992.             connected clients when the save is complete.
  993.  
  994.   Args:     IStream* pIStream
  995.               IStream interface pointer for stream to load from.
  996.             BOOL bClearDirty
  997.               Determines if this method should clear the COM object's
  998.               dirty flag. If bClearDirty is TRUE then clear the dirty
  999.               flag.
  1000.  
  1001.   Returns:  HRESULT
  1002.               Standard result code. NOERROR for success.
  1003. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1004. STDMETHODIMP COPageList::CImpIPersistStream::Save(
  1005.                IStream* pIStream,
  1006.                BOOL bClearDirty)
  1007. {
  1008.   HRESULT hr = E_POINTER;
  1009.   ULONG ulToWrite, ulWritten;
  1010.  
  1011.   if (NULL != pIStream)
  1012.   {
  1013.     // Got a stream. Now write data into it.
  1014.     // First write PAGELISTPROPS structure.
  1015.     m_pCO->m_PageListProps.lPageListSize = m_pCO->m_lPageListEnd + 1;
  1016.     ulToWrite = sizeof(PAGELISTPROPS);
  1017.     hr = pIStream->Write(&m_pCO->m_PageListProps, ulToWrite, &ulWritten);
  1018.     if (SUCCEEDED(hr) && ulToWrite != ulWritten)
  1019.       hr = STG_E_CANTSAVE;
  1020.     if (SUCCEEDED(hr))
  1021.     {
  1022.       // Now write the complete array of Page List Data.
  1023.       ulToWrite = m_pCO->m_PageListProps.lPageListSize * sizeof(PAGEITEM);
  1024.       hr = pIStream->Write(m_pCO->m_paPageList, ulToWrite, &ulWritten);
  1025.       if (SUCCEEDED(hr) && ulToWrite != ulWritten)
  1026.         hr = STG_E_CANTSAVE;
  1027.       if (SUCCEEDED(hr))
  1028.       {
  1029.         // Clear this COM object's dirty flag if instructed. Otherwise,
  1030.         // preserve its content.
  1031.         if (bClearDirty)
  1032.           m_pCO->m_bDirty = FALSE;
  1033.       }
  1034.     }
  1035.   }
  1036.  
  1037.   // Notify all other connected clients that PageList is now saved.
  1038.   if (SUCCEEDED(hr))
  1039.     m_pCO->NotifySinks(PAGELIST_EVENT_SAVED,-1);
  1040.  
  1041.   return hr;
  1042. }
  1043.  
  1044.  
  1045. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1046.   Method:   COPageList::CImpIPersistStream::GetSizeMax
  1047.  
  1048.   Summary:  Called to obtain the maximum size of the chunk of data that
  1049.             could be saved when Save is next called.
  1050.  
  1051.   Args:     ULARGE_INTEGER* pcbSize
  1052.               Address of the caller's ULARGE_INTEGER variable to receive
  1053.               the data size that Save would next write into the stream.
  1054.  
  1055.   Returns:  HRESULT
  1056.               Standard result code. NOERROR for success.
  1057. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1058. STDMETHODIMP COPageList::CImpIPersistStream::GetSizeMax(
  1059.                ULARGE_INTEGER* pcbSize)
  1060. {
  1061.   HRESULT hr = E_POINTER;
  1062.   DWORD dwProps, dwData;
  1063.  
  1064.   if (NULL != pcbSize)
  1065.   {
  1066.     dwProps = sizeof(PAGELISTPROPS);
  1067.     dwData = (m_pCO->m_lPageListEnd+1) * sizeof(PAGEITEM);
  1068.     ULISet32(*pcbSize, dwProps+dwData);
  1069.     hr = NOERROR;
  1070.   }
  1071.  
  1072.   return hr;
  1073. }
  1074.  
  1075.  
  1076. /*---------------------------------------------------------------------------
  1077.   COPageList's nested implementation of the custom IPageList interface
  1078.   including Constructor, Destructor, QueryInterface, AddRef, Release,
  1079.   Get, Set, Add, Delete, and Clear.
  1080. ---------------------------------------------------------------------------*/
  1081.  
  1082. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1083.   Method:   COPageList::CImpIPageList::CImpIPageList
  1084.  
  1085.   Summary:  Constructor for the CImpIPageList interface instantiation.
  1086.  
  1087.   Args:     COPageList* pCO,
  1088.               Back pointer to the parent outer object.
  1089.             IUnknown* pUnkOuter
  1090.               Pointer to the outer Unknown.  For delegation.
  1091.  
  1092.   Modifies: m_pCO, m_pUnkOuter.
  1093.  
  1094.   Returns:  void
  1095. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1096. COPageList::CImpIPageList::CImpIPageList(
  1097.   COPageList* pCO,
  1098.   IUnknown* pUnkOuter)
  1099. {
  1100.   // Init the Main Object Pointer to point to the parent object.
  1101.   m_pCO = pCO;
  1102.  
  1103.   // Init the CImpIPageList interface's delegating Unknown pointer.  We
  1104.   // use the Main Object pointer for IUnknown delegation here if we are
  1105.   // not being aggregated.  If we are being aggregated we use the supplied
  1106.   // pUnkOuter for IUnknown delegation.  In either case the pointer
  1107.   // assignment requires no AddRef because the CImpIPageList lifetime is
  1108.   // quaranteed by the lifetime of the parent object in which
  1109.   // CImpIPageList is nested.
  1110.   if (NULL == pUnkOuter)
  1111.     m_pUnkOuter = pCO;
  1112.   else
  1113.     m_pUnkOuter = pUnkOuter;
  1114.  
  1115.   return;
  1116. }
  1117.  
  1118.  
  1119. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1120.   Method:   COPageList::CImpIPageList::~CImpIPageList
  1121.  
  1122.   Summary:  Destructor for the CImpIPageList interface instantiation.
  1123.  
  1124.   Args:     void
  1125.  
  1126.   Returns:  void
  1127. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1128. COPageList::CImpIPageList::~CImpIPageList(void)
  1129. {
  1130.  
  1131.   return;
  1132. }
  1133.  
  1134.  
  1135. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1136.   Method:   COPageList::CImpIPageList::QueryInterface
  1137.  
  1138.   Summary:  The QueryInterface IUnknown member of this IPageList interface
  1139.             implementation that delegates to m_pUnkOuter, whatever it is.
  1140.  
  1141.   Args:     REFIID riid,
  1142.               [in] GUID of the Interface being requested.
  1143.             PPVOID ppv)
  1144.               [out] Address of the caller's pointer variable that will
  1145.               receive the requested interface pointer.
  1146.  
  1147.   Returns:  HRESULT
  1148.               Standard result code. NOERROR for success.
  1149.               Returned by the delegated outer QueryInterface call.
  1150. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1151. STDMETHODIMP COPageList::CImpIPageList::QueryInterface(
  1152.                REFIID riid,
  1153.                PPVOID ppv)
  1154. {
  1155.   // Delegate this call to the outer object's QueryInterface.
  1156.   return m_pUnkOuter->QueryInterface(riid, ppv);
  1157. }
  1158.  
  1159.  
  1160. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1161.   Method:   COPageList::CImpIPageList::AddRef
  1162.  
  1163.   Summary:  The AddRef IUnknown member of this IPageList interface
  1164.             implementation that delegates to m_pUnkOuter, whatever it is.
  1165.  
  1166.   Args:     void
  1167.  
  1168.   Returns:  ULONG
  1169.               Returned by the delegated outer AddRef call.
  1170. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1171. STDMETHODIMP_(ULONG) COPageList::CImpIPageList::AddRef(void)
  1172. {
  1173.   // Delegate this call to the outer object's AddRef.
  1174.   return m_pUnkOuter->AddRef();
  1175. }
  1176.  
  1177.  
  1178. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1179.   Method:   COPageList::CImpIPageList::Release
  1180.  
  1181.   Summary:  The Release IUnknown member of this IPageList interface
  1182.             implementation that delegates to m_pUnkOuter, whatever it is.
  1183.  
  1184.   Args:     void
  1185.  
  1186.   Returns:  ULONG
  1187.               Returned by the delegated outer Release call.
  1188. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1189. STDMETHODIMP_(ULONG) COPageList::CImpIPageList::Release(void)
  1190. {
  1191.   // Delegate this call to the outer object's Release.
  1192.   return m_pUnkOuter->Release();
  1193. }
  1194.  
  1195.  
  1196. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1197.   Method:   COPageList::CImpIPageList::FindSlot
  1198.  
  1199.   Summary:  An internal private utility member method to find the PageList
  1200.             array slot index of a specified page sequence number.
  1201.  
  1202.   Args:     INT iPage
  1203.               Requested Page number of the item to find.
  1204.             LONG* plSlot
  1205.               Address of variable to receive slot index of found page.
  1206.  
  1207.   Returns:  HRESULT
  1208.               Standard result code. NOERROR for success.
  1209. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1210. HRESULT COPageList::CImpIPageList::FindSlot(
  1211.           INT iPage,
  1212.           LONG* plSlot)
  1213. {
  1214.   HRESULT hr = E_FAIL;
  1215.   LONG i;
  1216.  
  1217.   // Loop thru the current PageList array and find the requested
  1218.   // page number.
  1219.   for (i=0; i<=m_pCO->m_lPageListEnd; i++)
  1220.   {
  1221.     if (PAGETYPE_NONE != m_pCO->m_paPageList[i].nType &&
  1222.         iPage == m_pCO->m_paPageList[i].iPage)
  1223.     {
  1224.       *plSlot = i;
  1225.       hr = NOERROR;
  1226.       break;
  1227.     }
  1228.   }
  1229.  
  1230.   return hr;
  1231. }
  1232.  
  1233.  
  1234. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1235.   Method:   COPageList::CImpIPageList::NextSlot
  1236.  
  1237.   Summary:  An internal private utility member method to obtain the next
  1238.             slot in the dynamic PageList array. If the requested page
  1239.             number lies within the existing ordered sequence, it is
  1240.             inserted before the current item of the same page number. If
  1241.             the requested page number is < 0 then the new item is assumed
  1242.             to be a new last page number. This method pre-assigns the
  1243.             expected page number of the item. NextSlot will expand the
  1244.             dynamic array for more entries if needed.
  1245.  
  1246.   Args:     INT iPage
  1247.               Requested Page order number of the new addition. If this
  1248.               page number is < 0 then add new item to end of list.
  1249.             LONG* plSlot
  1250.               Address of slot variable to receive index of new slot.
  1251.  
  1252.   Modifies: m_lPageListEnd, m_lPageListMax, m_paPageList.
  1253.  
  1254.   Returns:  HRESULT
  1255.               Standard result code. NOERROR for success.
  1256. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1257. HRESULT COPageList::CImpIPageList::NextSlot(
  1258.           INT iPage,
  1259.           LONG* plSlot)
  1260. {
  1261.   HRESULT hr = E_FAIL;
  1262.   PAGEITEM* paPgList;
  1263.   LONG i, lNewEnd = m_pCO->m_lPageListEnd;
  1264.   LONG lSlot = 0;
  1265.   SHORT nLastPage;
  1266.  
  1267.   // Loop thru the current PageList array and find first empty slot.
  1268.   for (i=0; i<=lNewEnd; i++)
  1269.   {
  1270.     if (PAGETYPE_NONE == m_pCO->m_paPageList[i].nType)
  1271.     {
  1272.       // Assign new slot number as first empty slot found.
  1273.       lSlot = i;
  1274.       hr = NOERROR;
  1275.       break;
  1276.     }
  1277.   }
  1278.  
  1279.   if (FAILED(hr))
  1280.   {
  1281.     hr = NOERROR;
  1282.     lNewEnd++;
  1283.  
  1284.     // No empty slots so expand the array.
  1285.     if (lNewEnd >= m_pCO->m_lPageListMax)
  1286.     {
  1287.       // No more room in PageList array. Allocate new array space.
  1288.       paPgList = new PAGEITEM[(LONG)(m_pCO->m_lPageListMax+PAGELIST_ALLOC)];
  1289.       if (NULL != paPgList)
  1290.       {
  1291.         // Copy the content of the old full array to the new larger array.
  1292.         memcpy(paPgList, m_pCO->m_paPageList, lNewEnd * sizeof(PAGEITEM));
  1293.  
  1294.         // Zero (& mark as empty) the expanded portion of the new array.
  1295.         memset(
  1296.           &(paPgList[lNewEnd]),
  1297.           0,
  1298.           PAGELIST_ALLOC * sizeof(PAGEITEM));
  1299.  
  1300.         // New larger array is ready--delete the old array.
  1301.         if (NULL != m_pCO->m_paPageList)
  1302.           delete [] m_pCO->m_paPageList;
  1303.  
  1304.         // Rig the PageList to use the new larger array.
  1305.         m_pCO->m_paPageList = paPgList;
  1306.  
  1307.         // Calculate the new max index.
  1308.         m_pCO->m_lPageListMax += PAGELIST_ALLOC;
  1309.       }
  1310.       else
  1311.         hr = E_OUTOFMEMORY;
  1312.     }
  1313.  
  1314.     if (SUCCEEDED(hr))
  1315.     {
  1316.       // Assign new List End.
  1317.       m_pCO->m_lPageListEnd = lNewEnd;
  1318.  
  1319.       // Assign new slot number as last item of new item list.
  1320.       lSlot = lNewEnd;
  1321.     }
  1322.   }
  1323.  
  1324.   if (SUCCEEDED(hr))
  1325.   {
  1326.     if (iPage < 0)
  1327.     {
  1328.       nLastPage = -1;
  1329.  
  1330.       // Find the last page number.
  1331.       for (i=0; i<=lNewEnd; i++)
  1332.       {
  1333.         if (PAGETYPE_NONE != m_pCO->m_paPageList[i].nType
  1334.             && m_pCO->m_paPageList[i].iPage > nLastPage)
  1335.           nLastPage = m_pCO->m_paPageList[i].iPage;
  1336.       }
  1337.  
  1338.       // Assign the Page Number of the new last entry.
  1339.       m_pCO->m_paPageList[lSlot].iPage = nLastPage + 1;
  1340.     }
  1341.     else
  1342.     {
  1343.       // Renumber the existing page items to make room for the new one.
  1344.       for (i=0; i<=lNewEnd; i++)
  1345.       {
  1346.         if (PAGETYPE_NONE != m_pCO->m_paPageList[i].nType
  1347.             && iPage >= m_pCO->m_paPageList[i].iPage)
  1348.           m_pCO->m_paPageList[i].iPage += 1;
  1349.       }
  1350.  
  1351.       // Assign the Page Number.
  1352.       m_pCO->m_paPageList[lSlot].iPage = iPage;
  1353.     }
  1354.  
  1355.     // Assign the output slot number.
  1356.     *plSlot = lSlot;
  1357.   }
  1358.  
  1359.   return hr;
  1360. }
  1361.  
  1362.  
  1363. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1364.   Method:   COPageList::CImpIPageList::NextName
  1365.  
  1366.   Summary:  An internal private utility member method to generate the next
  1367.             internal name for the storage or stream that will be used to
  1368.             store pages.
  1369.  
  1370.   Args:     SHORT nType,
  1371.               Page Type of the new page.
  1372.             WCHAR* pwszName
  1373.               Pointer to the wide char string to receive the new name.
  1374.  
  1375.   Returns:  HRESULT
  1376.               Standard result code. NOERROR for success.
  1377. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1378. HRESULT COPageList::CImpIPageList::NextName(
  1379.           SHORT nType,
  1380.           WCHAR* pwszName)
  1381. {
  1382.   HRESULT hr = E_POINTER;
  1383.   TCHAR szName[PAGE_NAME_SIZE];
  1384.   TCHAR szNameCount[PAGE_NAME_SIZE];
  1385.  
  1386.   if (NULL != pwszName)
  1387.   {
  1388.     hr = NOERROR;
  1389.  
  1390.     // Assign the page type string.
  1391.     switch (nType)
  1392.     {
  1393.       case PAGETYPE_DRAWING:
  1394.         lstrcpy(szName, TEXT(DRAWING_STR));
  1395.         break;
  1396.       case PAGETYPE_TEXT:
  1397.         lstrcpy(szName, TEXT(TEXT_STR));
  1398.         break;
  1399.       default:
  1400.         hr = E_FAIL;
  1401.         break;
  1402.     }
  1403.  
  1404.     if (SUCCEEDED(hr))
  1405.     {
  1406.       // Calculate the next name count and make a name string.
  1407.  
  1408.       // Bump the page name counter.
  1409.       m_pCO->m_PageListProps.lPageNameCounter += 1;
  1410.  
  1411.       // This change means we should mark the PageList as dirty.
  1412.       // A save to file of the PageList object is now needed.
  1413.       m_pCO->m_bDirty = TRUE;
  1414.  
  1415. #ifdef UNICODE
  1416.       _ltow(m_pCO->m_PageListProps.lPageNameCounter, szNameCount, 10);
  1417.       lstrcat(szName, szNameCount);
  1418.       lstrcpy(pwszName, szName);
  1419. #else
  1420.       _ltoa(m_pCO->m_PageListProps.lPageNameCounter, szNameCount, 10);
  1421.       lstrcat(szName, szNameCount);
  1422.       // Convert to WideChar if we are NOT compiled for Unicode.
  1423.       AnsiToUc(szName, pwszName, PAGE_NAME_SIZE);
  1424. #endif
  1425.     }
  1426.   }
  1427.  
  1428.   return hr;
  1429. }
  1430.  
  1431.  
  1432. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1433.   Method:   COPageList::CImpIPageList::Get
  1434.  
  1435.   Summary:  Get a Page List Item for a given page number. Get back
  1436.             open status, page type, page title, and page data name.
  1437.  
  1438.   Args:     INT iPage,
  1439.               Page sequence number of the item to get (0-based).
  1440.             BOOL* pbOpen,
  1441.               Address of a boolean variable to receive the page open status.
  1442.               If NULL then don't get this value.
  1443.             SHORT* pnType,
  1444.               Address of a PageType variable to receive the page type.
  1445.               If NULL then don't get this value.
  1446.             WCHAR* pwszTitle,
  1447.               Address of a wide character string to receive the page title.
  1448.               If NULL then don't get this value.
  1449.             WCHAR* pwszDataName)
  1450.               Address of a wide character string to receive the data name.
  1451.               If NULL then don't get this value.
  1452.  
  1453.   Returns:  HRESULT
  1454.               Standard result code. NOERROR for success.
  1455. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1456. STDMETHODIMP COPageList::CImpIPageList::Get(
  1457.                INT     iPage,
  1458.                BOOL*   pbOpen,
  1459.                SHORT*  pnType,
  1460.                WCHAR*  pwszTitle,
  1461.                WCHAR*  pwszDataName)
  1462. {
  1463.   HRESULT hr = E_POINTER;
  1464.   LONG lSlot;
  1465.  
  1466.   hr = FindSlot(iPage, &lSlot);
  1467.   if (SUCCEEDED(hr))
  1468.   {
  1469.     // Assign the caller's open status variable.
  1470.     if (NULL != pbOpen)
  1471.       *pbOpen = m_pCO->m_paPageList[lSlot].bOpen;
  1472.  
  1473.     // Assign the caller's page type variable.
  1474.     if (NULL != pnType)
  1475.       *pnType = m_pCO->m_paPageList[lSlot].nType;
  1476.  
  1477.     // Copy the page title to the caller's variable.
  1478.     if (NULL != pwszTitle)
  1479.       memcpy(
  1480.         pwszTitle,
  1481.         m_pCO->m_paPageList[lSlot].wszTitle,
  1482.         PAGE_TITLE_SIZE*sizeof(WCHAR));
  1483.  
  1484.     // Copy the page data name to the caller's variable.
  1485.     if (NULL != pwszDataName)
  1486.       memcpy(
  1487.         pwszDataName,
  1488.         m_pCO->m_paPageList[lSlot].wszDataName,
  1489.         PAGE_NAME_SIZE*sizeof(WCHAR));
  1490.   }
  1491.  
  1492.   return hr;
  1493. }
  1494.  
  1495.  
  1496. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1497.   Method:   COPageList::CImpIPageList::Set
  1498.  
  1499.   Summary:  Set a Page List Item of specified page number with a
  1500.             new Title and the page's current open status.
  1501.  
  1502.   Args:     INT iPage
  1503.               Page sequence number of the item to put (0-based).
  1504.             SHORT   nOpenStatus,
  1505.               Status of whether the page is open or not.
  1506.             WCHAR* pwszNewTitle);
  1507.               New Title for the page.
  1508.  
  1509.   Returns:  HRESULT
  1510.               Standard result code. NOERROR for success.
  1511. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1512. STDMETHODIMP COPageList::CImpIPageList::Set(
  1513.                INT     iPage,
  1514.                SHORT   nOpenStatus,
  1515.                WCHAR*  pwszNewTitle)
  1516. {
  1517.   HRESULT hr = E_FAIL;
  1518.   LONG lSlot;
  1519.  
  1520.   if (NULL != pwszNewTitle || OPENSTATE_NONE != nOpenStatus)
  1521.   {
  1522.     hr = FindSlot(iPage, &lSlot);
  1523.     if (SUCCEEDED(hr))
  1524.     {
  1525.       switch (nOpenStatus)
  1526.       {
  1527.         case OPENSTATE_YES:
  1528.           m_pCO->m_paPageList[lSlot].bOpen = TRUE;
  1529.           break;
  1530.         case OPENSTATE_NO:
  1531.           m_pCO->m_paPageList[lSlot].bOpen = FALSE;
  1532.           break;
  1533.         default:
  1534.           break;
  1535.       }
  1536.  
  1537.       if (NULL != pwszNewTitle)
  1538.       {
  1539.         // Copy the caller's new title into the specified list item.
  1540.         memcpy(
  1541.           m_pCO->m_paPageList[lSlot].wszTitle,
  1542.           pwszNewTitle,
  1543.           PAGE_TITLE_SIZE*sizeof(WCHAR));
  1544.       }
  1545.  
  1546.       // Mark PageList as dirty. Save to file is needed.
  1547.       m_pCO->m_bDirty = TRUE;
  1548.  
  1549.       // Notify all other connected clients that a page list
  1550.       // item was set/changed.
  1551.       m_pCO->NotifySinks(PAGELIST_EVENT_PAGESET, iPage);
  1552.     }
  1553.   }
  1554.  
  1555.   return hr;
  1556. }
  1557.  
  1558.  
  1559. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1560.   Method:   COPageList::CImpIPageList::Add
  1561.  
  1562.   Summary:  Add/Insert a new Page List Item of specified type with
  1563.             specified title. Inserts before the entry with the
  1564.             specified page number. If iPage < 0 then add the new item
  1565.             to the end of the list.
  1566.  
  1567.   Args:     INT     iPage,
  1568.               Page sequence number to insert. If < 0 then add to end of
  1569.               page number sequence and assign *piNewPage with the new
  1570.               page number.
  1571.             SHORT   nType,
  1572.               Page Type of the new page.
  1573.             WCHAR*  pwszTitle,
  1574.               Pointer to the wide character string that will be the new
  1575.               page's title.
  1576.             INT* piNewPage
  1577.               Address of integer variable to receive new Page number.
  1578.  
  1579.   Returns:  HRESULT
  1580.               Standard result code. NOERROR for success.
  1581. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1582. STDMETHODIMP COPageList::CImpIPageList::Add(
  1583.                INT iPage,
  1584.                SHORT nType,
  1585.                WCHAR* pwszTitle,
  1586.                INT* piNewPage)
  1587. {
  1588.   HRESULT hr = E_POINTER;
  1589.   LONG lSlot;
  1590.  
  1591.   if (NULL != pwszTitle && PAGETYPE_NONE != nType)
  1592.   {
  1593.     // Find the next slot and assign its page number.
  1594.     hr = NextSlot(iPage, &lSlot);
  1595.     if (SUCCEEDED(hr))
  1596.     {
  1597.       // Create and assign a new storage/stream name.
  1598.       hr = NextName(nType, m_pCO->m_paPageList[lSlot].wszDataName);
  1599.       if (SUCCEEDED(hr))
  1600.       {
  1601.         // Copy the caller's new title into the specified page list item.
  1602.         memcpy(
  1603.           m_pCO->m_paPageList[lSlot].wszTitle,
  1604.           pwszTitle,
  1605.           PAGE_TITLE_SIZE*sizeof(WCHAR));
  1606.  
  1607.         // Assign the new page type.
  1608.         m_pCO->m_paPageList[lSlot].nType = nType;
  1609.  
  1610.         // Inform the caller of the new page number.
  1611.         *piNewPage = m_pCO->m_paPageList[lSlot].iPage;
  1612.  
  1613.         // Mark PageList as dirty. Save to file is needed.
  1614.         m_pCO->m_bDirty = TRUE;
  1615.  
  1616.         // Notify all other connected clients that a new page list
  1617.         // item was added.
  1618.         m_pCO->NotifySinks(
  1619.                   PAGELIST_EVENT_PAGEADDED,
  1620.                   m_pCO->m_paPageList[lSlot].iPage);
  1621.       }
  1622.     }
  1623.   }
  1624.  
  1625.   return hr;
  1626. }
  1627.  
  1628.  
  1629. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1630.   Method:   COPageList::CImpIPageList::Delete
  1631.  
  1632.   Summary:  Delete a Page List Item for the specified page number.
  1633.  
  1634.   Args:     INT iPage
  1635.               Page number of the page item to delete (0-based).
  1636.  
  1637.   Returns:  HRESULT
  1638.               Standard result code. NOERROR for success.
  1639. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1640. STDMETHODIMP COPageList::CImpIPageList::Delete(
  1641.                INT iPage)
  1642. {
  1643.   HRESULT hr = E_FAIL;
  1644.   INT iLastPage = iPage;
  1645.   INT iPg;
  1646.   LONG lSlot, i;
  1647.  
  1648.   hr = FindSlot(iPage, &lSlot);
  1649.   if (SUCCEEDED(hr))
  1650.   {
  1651.     // Mark item as empty.
  1652.     m_pCO->m_paPageList[lSlot].nType = PAGETYPE_NONE;
  1653.  
  1654.     // Renumber the remaining pages to account for the missing page.
  1655.     for (i=0; i<=m_pCO->m_lPageListEnd; i++)
  1656.     {
  1657.       iPg = m_pCO->m_paPageList[i].iPage;
  1658.       if (PAGETYPE_NONE != m_pCO->m_paPageList[i].nType
  1659.           && iPg >= 0 && iPg > iPage)
  1660.       {
  1661.         if (iPg > 0)
  1662.           iPg--;
  1663.         m_pCO->m_paPageList[i].iPage = iPg;
  1664.         if (iPg > iLastPage)
  1665.           iLastPage = iPg;
  1666.       }
  1667.     }
  1668.  
  1669.     // Mark PageList as dirty.
  1670.     m_pCO->m_bDirty = TRUE;
  1671.  
  1672.     // Notify all other connected clients that a page list
  1673.     // item was deleted.
  1674.     iPg = (iPage <= iLastPage) ? iPage : iLastPage;
  1675.     m_pCO->NotifySinks(PAGELIST_EVENT_PAGEDELETED, iPg);
  1676.   }
  1677.  
  1678.   return hr;
  1679. }
  1680.  
  1681.  
  1682. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1683.   Method:   COPageList::CImpIPageList::Clear
  1684.  
  1685.   Summary:  Clears the entire page list. Removes all entries.
  1686.  
  1687.   Args:     none.
  1688.  
  1689.   Returns:  HRESULT
  1690.               Standard result code. NOERROR for success.
  1691. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1692. STDMETHODIMP COPageList::CImpIPageList::Clear(
  1693.                void)
  1694. {
  1695.   HRESULT hr = E_FAIL;
  1696.  
  1697.   hr = m_pCO->Clear();
  1698.  
  1699.   // Notify all other connected clients that the list was cleared.
  1700.   if (SUCCEEDED(hr))
  1701.     m_pCO->NotifySinks(PAGELIST_EVENT_CLEARED,0);
  1702.  
  1703.   return hr;
  1704. }
  1705.