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