home *** CD-ROM | disk | FTP | other *** search
/ PC Press 1997 July / Sezamfile97_2.iso / windows / program / activex / axtsamp.exe / TSBRANCH.EXE / STOSERVE / PAPER.CPP < prev    next >
C/C++ Source or Header  |  1996-12-29  |  54KB  |  1,623 lines

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