GUIPAPER.CPP
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:36k
源码类别:

Windows编程

开发平台:

Visual C++

  1. /*+==========================================================================
  2.   File:      GUIPAPER.CPP
  3.   Summary:   Implementation file for the CGuiPaper C++ class. A GuiPaper
  4.              is a C++ object that displays mouse movement as free-form
  5.              drawing in the client area of a designated window (much like
  6.              common scribble sample programs except that COM technology is
  7.              used throughout to construct this functionality). CGuiPaper
  8.              is anchored to the Windows GUI (Graphical User Interface)
  9.              environment--it retains knowledge of window handles and
  10.              device contexts on the local machine. This GuiPaper object
  11.              relies on a virtual paper object for storage of the drawing
  12.              data. This virtual Paper object (a COPaper) is instantiated
  13.              as a COM object in a separate thread-safe out-of-process
  14.              server, DCDSERVE.
  15.              For a comprehensive tutorial code tour of GUIPAPER's contents
  16.              and offerings see the tutorial DCOMDRAW.HTM file. For more
  17.              specific technical details on the internal workings see the
  18.              comments dispersed throughout the GUIPAPER source code.
  19.   Classes:   CGuiPaper.
  20.   Origin:    8-23-97: atrent - Editor inheritance from GUIBALL.CPP in the
  21.              CONCLIEN source. [Revised]
  22. ----------------------------------------------------------------------------
  23.   This file is part of the Microsoft COM Tutorial Code Samples.
  24.   Copyright (C) Microsoft Corporation, 1997.  All rights reserved.
  25.   This source code is intended only as a supplement to Microsoft
  26.   Development Tools and/or on-line documentation.  See these other
  27.   materials for detailed information regarding Microsoft code samples.
  28.   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  29.   KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  30.   IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  31.   PARTICULAR PURPOSE.
  32. ==========================================================================+*/
  33. /*--------------------------------------------------------------------------
  34.   We include WINDOWS.H for all Win32 applications.
  35.   We include OLE2.H because we will be calling the COM/OLE Libraries.
  36.   We include OLECTL.H because it has definitions for connectable objects.
  37.   We include COMMDLG.H because we will be using the Open File,
  38.     Choose Color, and potentially other Common dialogs.
  39.   We include TCHAR.H for general Unicode/Ansi prototype of utility
  40.     functions like _tsplitpath, etc.
  41.   We include APPUTIL.H because we will be building this application using
  42.     the convenient Virtual Window and Dialog classes and other
  43.     utility functions in the APPUTIL Library (ie, APPUTIL.LIB).
  44.   We include PAPINT.H and PAPGUIDS.H for the common Paper-related Interface
  45.     class, GUID, and CLSID specifications.
  46.   We include GUIPAPER.H because it has the C++ class used for GUI display
  47.     of the drawing Paper.
  48.   We include SINK.H because it has the C++ class used for the sink that
  49.     receives event notifications from the COPaper object in the server.
  50.   We include DCOMDRAW.H because it has class and resource definitions
  51.     specific to this DCOMDRAW application.
  52. ---------------------------------------------------------------------------*/
  53. #include <windows.h>
  54. #include <ole2.h>
  55. #include <olectl.h>
  56. #include <commdlg.h>
  57. #include <tchar.h>
  58. #include <apputil.h>
  59. #include <papint.h>
  60. #include <papguids.h>
  61. #include "guipaper.h"
  62. #include "sink.h"
  63. #include "dcomdraw.h"
  64. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  65.   Method:   CGuiPaper::CGuiPaper
  66.   Summary:  Constructor.
  67.   Args:     void
  68.   Returns:  void
  69. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  70. CGuiPaper::CGuiPaper(void)
  71. {
  72.   m_hWnd       = NULL;
  73.   m_hInst      = NULL;
  74.   m_hDC        = NULL;
  75.   m_pISharePaper = NULL;
  76.   m_hPen       = NULL;
  77.   m_nInkWidth  = INK_THIN;
  78.   m_crInkColor = RGB(0,0,0);
  79.   m_bInkSaving = FALSE;
  80.   m_bInking    = FALSE;
  81.   m_bPainting  = FALSE;
  82.   m_OldPos.x   = 0;
  83.   m_OldPos.y   = 0;
  84.   m_pCOPaperSink = NULL;
  85.   m_dwPaperSink = 0;
  86.   m_bDirty     = FALSE;
  87.   m_PenCur = PENCUR_OFF | PENCUR_THIN;
  88.   m_hPenCurN = NULL;
  89.   m_hPenCurT = NULL;
  90.   m_hPenCurM = NULL;
  91.   m_hPenCurF = NULL;
  92. }
  93. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  94.   Method:   CGuiPaper::~CGuiPaper
  95.   Summary:  Destructor.
  96.   Args:     void
  97.   Returns:  void
  98. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  99. CGuiPaper::~CGuiPaper(void)
  100. {
  101.   BOOL bOk = TRUE;
  102.   if (m_pISharePaper)
  103.   {
  104.     // Just to make sure, turn off Ink Saving.
  105.     m_bInkSaving = FALSE;
  106.     // Make sure we unlock the paper object.
  107.     m_pISharePaper->Lock(FALSE);
  108.     // Disconnect all Sinks--currently only one: PaperSink. This officially
  109.     // stops all PaperSink notifications.
  110.     DisconnectPaperSink();
  111.     // Delete the Pen object.
  112.     if (m_hPen)
  113.       DeleteObject(m_hPen);
  114.     // Release the reference to the PaperSink object.
  115.     RELEASE_INTERFACE(m_pCOPaperSink);
  116.     // Release the main interface pointer copy held in CGuiPaper.
  117.     RELEASE_INTERFACE(m_pISharePaper);
  118.   }
  119. }
  120. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  121.   Method:   CGuiPaper::Init
  122.   Summary:  Get CGuiPaper started. Make any subordinate objects, like
  123.             COPaper and CPapFile, and get them started.
  124.   Args:     HINSTANCE hInst
  125.               Handle to the application instance.
  126.             HWND hWnd
  127.               Handle of the display window. Part of what makes CGuiPaper
  128.               a GUI kind of thing.
  129.   Returns:  BOOL
  130.               TRUE for success; FALSE for fail.
  131. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  132. BOOL CGuiPaper::Init(
  133.        HINSTANCE hInst,
  134.        HWND hWnd)
  135. {
  136.   BOOL bOk = FALSE;
  137.   HRESULT hr;
  138.   BOOL bFirst;
  139.   COPaperSink* pCobSink = NULL;
  140.   HCURSOR hCurPrev;
  141.   HCURSOR hCurWait = LoadCursor(NULL, IDC_WAIT);
  142.   if (hInst && hWnd)
  143.   {
  144.     m_hInst = hInst;
  145.     m_hWnd = hWnd;
  146.     // Load the pen cursors.
  147.     m_hPenCurN = LoadCursor(m_hInst, TEXT("PenCurN"));
  148.     m_hPenCurT = LoadCursor(m_hInst, TEXT("PenCurT"));
  149.     m_hPenCurM = LoadCursor(m_hInst, TEXT("PenCurM"));
  150.     m_hPenCurF = LoadCursor(m_hInst, TEXT("PenCurF"));
  151.     // Get and save our private display Device Context.
  152.     m_hDC = GetDC(m_hWnd);
  153.     // Change cursor to the hour glass. Init could take awhile.
  154.     hCurPrev = SetCursor(hCurWait);
  155.     // Call COM service to create a COPaper instance. We are not
  156.     // aggregating it so we ask for its ISharePaper interface directly.
  157.     hr = CoCreateInstance(
  158.            CLSID_SharePaper,
  159.            NULL,
  160.            CLSCTX_LOCAL_SERVER,
  161.            IID_ISharePaper,
  162.            (PPVOID)&m_pISharePaper);
  163.     if (SUCCEEDED(hr))
  164.     {
  165.       // Init the COPaper object.
  166.       GetClientRect(hWnd, &m_WinRect);
  167.       hr = m_pISharePaper->InitPaper(&m_WinRect, &bFirst);
  168.       if (SUCCEEDED(hr))
  169.       {
  170.         // Resize this client's window to match what the COPaper
  171.         // object returned for its size.
  172.         ResizeWin(m_WinRect.right, m_WinRect.bottom);
  173.         // Create the COPaperSink object to receive COPaper events.
  174.         pCobSink = new COPaperSink(NULL, this);
  175.         if (NULL != pCobSink)
  176.         {
  177.           // Save a pointer to the COPaperSink IUnknown interface.
  178.           // AddRef because of this saved copy.
  179.           m_pCOPaperSink = pCobSink;
  180.           m_pCOPaperSink->AddRef();
  181.         }
  182.         else
  183.           hr = E_OUTOFMEMORY;
  184.       }
  185.       bOk = SUCCEEDED(hr);
  186.     }
  187.     else
  188.       HrMsg(hWnd, TEXT(LOCAL_CREATE_ERR_STR), hr);
  189.     // Set Cursor back to what it was.
  190.     SetCursor(hCurPrev);
  191.   }
  192.   return (bOk);
  193. }
  194. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  195.   Method:   CGuiPaper::SetPenCur
  196.   Summary:  Set the pen cursor.
  197.   Args:     USHORT usPenCurNew
  198.               Or'd bits for the pen cursor.
  199.   Returns:  HRESULT
  200.               Standard result code. NOERROR for success.
  201. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  202. HRESULT CGuiPaper::SetPenCur(
  203.           USHORT usPenCurNew)
  204. {
  205.   HRESULT hr = E_FAIL;
  206.   USHORT usPenCurW;
  207.   usPenCurW = m_PenCur & (PENCUR_THIN | PENCUR_MEDIUM | PENCUR_THICK);
  208.   switch (usPenCurNew)
  209.   {
  210.     case PENCUR_OFF:
  211.      hr = (m_PenCur & PENCUR_ON) ? NOERROR : E_FAIL;
  212.      m_PenCur = usPenCurW;
  213.      SetClassLong(m_hWnd, GCL_HCURSOR, (LONG)m_hPenCurN);
  214.      break;
  215.     case PENCUR_ON:
  216.      hr = (m_PenCur & PENCUR_ON) ? E_FAIL: NOERROR;
  217.      m_PenCur = PENCUR_ON | usPenCurW;
  218.      switch (usPenCurW)
  219.      {
  220.        case PENCUR_THICK:
  221.          SetClassLong(m_hWnd, GCL_HCURSOR, (LONG)m_hPenCurF);
  222.          break;
  223.        case PENCUR_MEDIUM:
  224.          SetClassLong(m_hWnd, GCL_HCURSOR, (LONG)m_hPenCurM);
  225.          break;
  226.        case PENCUR_THIN:
  227.        default:
  228.          SetClassLong(m_hWnd, GCL_HCURSOR, (LONG)m_hPenCurT);
  229.          break;
  230.      }
  231.      break;
  232.     case PENCUR_THICK:
  233.      hr = (usPenCurW & PENCUR_THICK) ? E_FAIL: NOERROR;
  234.      m_PenCur = (m_PenCur & PENCUR_ON);
  235.      m_PenCur |= PENCUR_THICK;
  236.      SetClassLong(m_hWnd, GCL_HCURSOR, (LONG)m_hPenCurF);
  237.      break;
  238.     case PENCUR_MEDIUM:
  239.      hr = (usPenCurW & PENCUR_MEDIUM) ? E_FAIL: NOERROR;
  240.      m_PenCur = (m_PenCur & PENCUR_ON);
  241.      m_PenCur |= PENCUR_MEDIUM;
  242.      SetClassLong(m_hWnd, GCL_HCURSOR, (LONG)m_hPenCurM);
  243.      break;
  244.     case PENCUR_THIN:
  245.      hr = (usPenCurW & PENCUR_THIN) ? E_FAIL: NOERROR;
  246.      m_PenCur = (m_PenCur & PENCUR_ON);
  247.      m_PenCur |= PENCUR_THIN;
  248.      SetClassLong(m_hWnd, GCL_HCURSOR, (LONG)m_hPenCurT);
  249.      break;
  250.     default:
  251.      break;
  252.   }
  253.   return hr;
  254. }
  255. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  256.   Method:   CGuiPaper::Lock
  257.   Summary:  Lock the paper object for drawing by this client.
  258.   Args:     BOOL bLock
  259.               TRUE => lock the paper. FALSE => unlock the paper.
  260.   Returns:  HRESULT
  261.               Standard result code. NOERROR for success.
  262. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  263. HRESULT CGuiPaper::Lock(
  264.           BOOL bLock)
  265. {
  266.   HRESULT hr = NOERROR;
  267.   HMENU hMenu = ::GetMenu(m_hWnd);
  268.   if (bLock)
  269.   {
  270.     // If we are not already locked for drawing then do so.
  271.     if (!m_bLocked)
  272.     {
  273.       hr = m_pISharePaper->Lock(TRUE);
  274.       if (SUCCEEDED(hr))
  275.       {
  276.         // Set Main Window Title.
  277.         SetWindowText(m_hWnd, TEXT(MAIN_WINDOW_MASTER_STR));
  278.         // Set the menu check marks.
  279.         ::CheckMenuItem(
  280.             hMenu,
  281.             IDM_DRAW_MASTER,
  282.             MF_BYCOMMAND | MF_CHECKED);
  283.         ::CheckMenuItem(
  284.             hMenu,
  285.             IDM_DRAW_SLAVE,
  286.             MF_BYCOMMAND | MF_UNCHECKED);
  287.         // Set the Pen cursor on.
  288.         SetPenCur(PENCUR_ON);
  289.         m_bLocked = TRUE;
  290.         // We have siezed control of the pen and can now allow ink saving.
  291.         m_bInkSaving = TRUE;
  292.         m_bDirty = TRUE;
  293.       }
  294.       else
  295.       {
  296.         // Set Main Window Title.
  297.         SetWindowText(m_hWnd, TEXT(MAIN_WINDOW_SLAVE_STR));
  298.         // Set the menu check marks.
  299.         ::CheckMenuItem(
  300.             hMenu,
  301.             IDM_DRAW_MASTER,
  302.             MF_BYCOMMAND | MF_UNCHECKED);
  303.         ::CheckMenuItem(
  304.             hMenu,
  305.             IDM_DRAW_SLAVE,
  306.             MF_BYCOMMAND | MF_CHECKED);
  307.         // Set the Pen cursor off.
  308.         SetPenCur(PENCUR_OFF);
  309.       }
  310.     }
  311.   }
  312.   else
  313.   {
  314.     // If we are not already unlocked for drawing then do so.
  315.     if (m_bLocked)
  316.     {
  317.       // AskSave();
  318.       hr = m_pISharePaper->Lock(FALSE);
  319.       if (SUCCEEDED(hr))
  320.       {
  321.         // Set Main Window Title.
  322.         SetWindowText(m_hWnd, TEXT(MAIN_WINDOW_SLAVE_STR));
  323.         // Set the menu check marks.
  324.         ::CheckMenuItem(
  325.             hMenu,
  326.             IDM_DRAW_MASTER,
  327.             MF_BYCOMMAND | MF_UNCHECKED);
  328.         ::CheckMenuItem(
  329.             hMenu,
  330.             IDM_DRAW_SLAVE,
  331.             MF_BYCOMMAND | MF_CHECKED);
  332.         // Set the Pen cursor off.
  333.         SetPenCur(PENCUR_OFF);
  334.         m_bLocked = FALSE;
  335.         m_bInkSaving = FALSE;
  336.         m_bDirty = FALSE;
  337.       }
  338.     }
  339.   }
  340.   return hr;
  341. }
  342. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  343.   Method:   CGuiPaper::Master
  344.   Summary:  Return TRUE if this client is the master client that owns
  345.             the drawing paper.
  346.   Args:     void.
  347.   Returns:  BOOL
  348.               TRUE => This client is master.  FALSE => Not master.
  349. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  350. BOOL CGuiPaper::Master(void)
  351. {
  352.   BOOL bMaster = m_bLocked && m_bInkSaving;
  353.   return bMaster;
  354. }
  355. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  356.   Method:   CGuiPaper::ClearWin
  357.   Summary:  Clear display window but don't erase drawn data.
  358.   Args:     void.
  359.   Returns:  HRESULT
  360.               Standard result code. NOERROR for success.
  361. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  362. HRESULT CGuiPaper::ClearWin(void)
  363. {
  364.   HRESULT hr = E_FAIL;
  365.   RECT  WinRect;
  366.   if(!m_bInking)
  367.   {
  368.     // Get our window's client area rectangle.
  369.     GetClientRect(m_hWnd, &WinRect);
  370.     // Fill that rectangle with pixels of default white paint.
  371.     FillRect(m_hDC, &WinRect, GETCLASSBRUSH(m_hWnd));
  372.     hr = NOERROR;
  373.   }
  374.   return hr;
  375. }
  376. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  377.   Method:   CGuiPaper::ResizeWin
  378.   Summary:  Resize display window.
  379.   Args:     LONG lWidth
  380.               New window width. Max X coord.
  381.             LONG lHeight
  382.               New window height. Max Y coord.
  383.   Returns:  HRESULT
  384.               Standard result code. NOERROR for success.
  385. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  386. HRESULT CGuiPaper::ResizeWin(
  387.                      LONG lWidth,
  388.                      LONG lHeight)
  389. {
  390.   HRESULT hr = NOERROR;
  391.   if(!m_bInking)
  392.   {
  393.     SetWindowPos(
  394.       m_hWnd,
  395.       HWND_TOP,
  396.       0,
  397.       0,
  398.       6+lWidth,
  399.       50+lHeight,
  400.       SWP_NOMOVE | SWP_NOZORDER);
  401.   }
  402.   return hr;
  403. }
  404. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  405.   Method:   CGuiPaper::PaintWin
  406.   Summary:  Repaints the current drawing in the drawing window.
  407.   Args:     void
  408.   Returns:  HRESULT
  409.               Standard result code. NOERROR for success.
  410. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  411. HRESULT CGuiPaper::PaintWin(void)
  412. {
  413.   HRESULT hr = E_FAIL;
  414.   COLORREF crInkColorTmp;
  415.   SHORT nInkWidthTmp;
  416.   BOOL bInkSavingTmp;
  417.   LONG i;
  418.   SHORT nInkType;
  419.   SHORT nX;
  420.   SHORT nY;
  421.   SHORT nInkWidth;
  422.   COLORREF crInkColor;
  423.   if (m_pISharePaper && !m_bPainting && !m_bInking)
  424.   {
  425.     hr = NOERROR;
  426.     m_bPainting = TRUE;
  427.     // Save and restore ink color and width since redraw otherwise
  428.     // ends up changing these values in CGuiPaper.
  429.     crInkColorTmp = m_crInkColor;
  430.     nInkWidthTmp = m_nInkWidth;
  431.     bInkSavingTmp = m_bInkSaving;
  432.     m_bInkSaving = FALSE;
  433.     for (i = 0; SUCCEEDED(hr); i++)
  434.     {
  435.       hr = m_pISharePaper->GetInk(
  436.              i,
  437.              &nInkType,
  438.              &nX,
  439.              &nY,
  440.              &nInkWidth,
  441.              &crInkColor);
  442.       if (SUCCEEDED(hr))
  443.       {
  444.         switch (nInkType)
  445.         {
  446.           case INKTYPE_START:
  447.             m_nInkWidth = nInkWidth;
  448.             m_crInkColor = crInkColor;
  449.             InkStart(nX, nY);
  450.             break;
  451.           case INKTYPE_DRAW:
  452.             InkDraw(nX, nY);
  453.             break;
  454.           case INKTYPE_STOP:
  455.             InkStop(nX, nY);
  456.             break;
  457.           default:
  458.             break;
  459.         }
  460.       }
  461.     }
  462.     m_nInkWidth = nInkWidthTmp;
  463.     m_crInkColor = crInkColorTmp;
  464.     m_bInkSaving = bInkSavingTmp;
  465.     m_bPainting = FALSE;
  466.     hr = NOERROR;
  467.   }
  468.   return hr;
  469. }
  470. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  471.   Method:   CGuiPaper::Erase
  472.   Summary:  Erase content of the drawing paper and clear display window.
  473.   Args:     void.
  474.   Returns:  HRESULT
  475.               Standard result code. NOERROR for success.
  476. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  477. HRESULT CGuiPaper::Erase(void)
  478. {
  479.   HRESULT hr = E_FAIL;
  480.   if (Master())
  481.   {
  482.     if (m_pISharePaper)
  483.       hr = m_pISharePaper->Erase();
  484.     if (SUCCEEDED(hr))
  485.     {
  486.       ClearWin();
  487.       m_bDirty = TRUE;
  488.     }
  489.   }
  490.   else
  491.     ClearWin();
  492.   return hr;
  493. }
  494. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  495.   Method:   CGuiPaper::Resize
  496.   Summary:  Resizes current drawing paper rectangle.
  497.   Args:     LONG lWidth
  498.               New window width. Max X coord.
  499.             LONG lHeight
  500.               New window height. Max Y coord.
  501.   Returns:  HRESULT
  502.               Standard result code. NOERROR for success.
  503. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  504. HRESULT CGuiPaper::Resize(
  505.                      LONG lWidth,
  506.                      LONG lHeight)
  507. {
  508.   HRESULT hr = NOERROR;
  509.   if (Master())
  510.   {
  511.     if (m_pISharePaper)
  512.     {
  513.       hr = m_pISharePaper->Resize(lWidth, lHeight);
  514.       if (SUCCEEDED(hr))
  515.         m_bDirty = TRUE;
  516.     }
  517.   }
  518.   else
  519.     ResizeWin(lWidth, lHeight);
  520.   if (SUCCEEDED(hr))
  521.   {
  522.     // Store the new window size.
  523.     m_WinRect.right = lWidth;
  524.     m_WinRect.bottom = lHeight;
  525.   }
  526.   return hr;
  527. }
  528. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  529.   Method:   CGuiPaper::InkWidth
  530.   Summary:  Changes current ink width and sets the visable pen cursor
  531.             based on the width.
  532.   Args:     SHORT nInkWidth
  533.               New ink width in pixels.
  534.   Returns:  HRESULT
  535.               Standard result code. NOERROR for success.
  536. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  537. HRESULT CGuiPaper::InkWidth(
  538.                      SHORT nInkWidth)
  539. {
  540.   HRESULT hr = E_FAIL;
  541.   switch (nInkWidth)
  542.   {
  543.     case INK_THIN:
  544.       if (!(m_PenCur & (PENCUR_THIN)))
  545.         hr = SetPenCur(PENCUR_THIN);
  546.       break;
  547.     case INK_MEDIUM:
  548.       if (!(m_PenCur & (PENCUR_MEDIUM)))
  549.         hr = SetPenCur(PENCUR_MEDIUM);
  550.       break;
  551.     case INK_THICK:
  552.       if (!(m_PenCur & (PENCUR_THICK)))
  553.         hr = SetPenCur(PENCUR_THICK);
  554.       break;
  555.     default:
  556.       break;
  557.   }
  558.   if (SUCCEEDED(hr))
  559.     m_nInkWidth = nInkWidth;
  560.   return hr;
  561. }
  562. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  563.   Method:   CGuiPaper::InkColor
  564.   Summary:  Changes current ink color.
  565.   Args:     COLORREF crInkColor
  566.               RGB color ref value (eg, RGB(0,0,0) is black).
  567.   Returns:  HRESULT
  568.               Standard result code. NOERROR for success.
  569. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  570. HRESULT CGuiPaper::InkColor(
  571.                      COLORREF crInkColor)
  572. {
  573.   m_crInkColor = crInkColor;
  574.   return NOERROR;
  575. }
  576. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  577.   Method:   CGuiPaper::InkStart
  578.   Summary:  Starts an ink drawing sequence in the current color.
  579.   Args:     SHORT nX,
  580.               X coordinate in window rectangle of start point.
  581.             SHORT nY
  582.               Y coordinate in window rectangle of start point.
  583.   Returns:  HRESULT
  584.               Standard result code. NOERROR for success.
  585. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  586. HRESULT CGuiPaper::InkStart(
  587.                      SHORT nX,
  588.                      SHORT nY)
  589. {
  590.   HRESULT hr = E_FAIL;
  591.   // Start an ink drawing sequence only if one is not in progress.
  592.   if (!m_bInking)
  593.   {
  594.     // Remember start position.
  595.     m_OldPos.x = nX;
  596.     m_OldPos.y = nY;
  597.     // Delete old pen.
  598.     if (m_hPen)
  599.       DeleteObject(m_hPen);
  600.     // Create and select the new drawing pen.
  601.     m_hPen = CreatePen(PS_SOLID, m_nInkWidth, m_crInkColor);
  602.     SelectObject(m_hDC, m_hPen);
  603.     hr = NOERROR;
  604.     // Ask the Paper object to mark the start of the ink drawing
  605.     // sequence in the current ink color.
  606.     if (m_pISharePaper && m_bInkSaving)
  607.     {
  608.       hr = m_pISharePaper->InkStart(
  609.               nX,
  610.               nY,
  611.               m_nInkWidth,
  612.               m_crInkColor);
  613.       // Capture the Mouse.
  614.       SetCapture(m_hWnd);
  615.       // We've modified the ink data--it is now "dirty" with
  616.       // respect to the compound file image. Set dirty flag.
  617.       m_bDirty = TRUE;
  618.     }
  619.     // Set inking flag to TRUE.
  620.     m_bInking = TRUE;
  621.   }
  622.   return hr;
  623. }
  624. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  625.   Method:   CGuiPaper::InkDraw
  626.   Summary:  Draws and saves ink data during the currently active ink
  627.             drawing sequence.
  628.   Args:     SHORT nX,
  629.               X coordinate in window rectangle of start point.
  630.             SHORT nY
  631.               Y coordinate in window rectangle of start point.
  632.   Returns:  HRESULT
  633.               Standard result code. NOERROR for success.
  634. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  635. HRESULT CGuiPaper::InkDraw(
  636.                      SHORT nX,
  637.                      SHORT nY)
  638. {
  639.   if (m_bInking)
  640.   {
  641.     // Start this ink line at previous old position.
  642.     MoveToEx(m_hDC, m_OldPos.x, m_OldPos.y, NULL);
  643.     // Assign new old position and draw the new line.
  644.     LineTo(m_hDC, m_OldPos.x = nX, m_OldPos.y = nY);
  645.     // Ask the Paper object to save this data.
  646.     if (m_bInkSaving)
  647.       m_pISharePaper->InkDraw(nX, nY);
  648.   }
  649.   return NOERROR;
  650. }
  651. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  652.   Method:   CGuiPaper::InkStop
  653.   Summary:  Stops the currently active ink drawing sequence.
  654.   Args:     SHORT nX,
  655.               X coordinate in window rectangle of start point.
  656.             SHORT nY
  657.               Y coordinate in window rectangle of start point.
  658.   Returns:  HRESULT
  659.               Standard result code. NOERROR for success.
  660. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  661. HRESULT CGuiPaper::InkStop(
  662.                      SHORT nX,
  663.                      SHORT nY)
  664. {
  665.   if (m_bInking)
  666.   {
  667.     // Start this ink line at previous old position.
  668.     MoveToEx(m_hDC, m_OldPos.x, m_OldPos.y, NULL);
  669.     // Draw the last line.
  670.     LineTo(m_hDC, nX, nY);
  671.     // Turn off inking.
  672.     m_bInking = FALSE;
  673.     // Ask the Paper object to mark the stop of the ink drawing sequence.
  674.     if (m_bInkSaving)
  675.     {
  676.       m_pISharePaper->InkStop(nX, nY);
  677.       // Free the mouse.
  678.       ReleaseCapture();
  679.     }
  680.   }
  681.   return NOERROR;
  682. }
  683. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  684.   Method:   CGuiPaper::GetConnectionPoint
  685.   Summary:  Internal private method to obtain a connection point interface.
  686.   Args:     REFIID riid
  687.               IID of the requested connection point Interface.
  688.   Returns:  IConnectionPoint*
  689.               Requested IConnectionPoint interface pointer. NULL if none.
  690. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  691. IConnectionPoint* CGuiPaper::GetConnectionPoint(
  692.                     REFIID riid)
  693. {
  694.   IConnectionPoint* pConnPoint = NULL;
  695.   IConnectionPointContainer* pConnPointContainer = NULL;
  696.   IConnectionPoint* pConnPt;
  697.   HRESULT hr;
  698.   // First query the object for its Connection Point Container. This
  699.   // essentially asks the object in the server if it is connectable.
  700.   hr = m_pISharePaper->QueryInterface(
  701.          IID_IConnectionPointContainer,
  702.          (PPVOID)&pConnPointContainer);
  703.   if SUCCEEDED(hr)
  704.   {
  705.     // Find the requested Connection Point. This AddRef's the
  706.     // returned pointer.
  707.     hr = pConnPointContainer->FindConnectionPoint(riid, &pConnPt);
  708.     if (SUCCEEDED(hr))
  709.       pConnPoint = pConnPt;
  710.     RELEASE_INTERFACE(pConnPointContainer);
  711.   }
  712.   return pConnPoint;
  713. }
  714. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  715.   Method:   CGuiPaper::ConnectPaperSink
  716.   Summary:  Connect the PaperSink to the server COPaper source.
  717.   Args:     void
  718.   Returns:  HRESULT
  719.               Standard result code. NOERROR for success.
  720. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  721. HRESULT CGuiPaper::ConnectPaperSink(void)
  722. {
  723.   HRESULT hr = E_FAIL;
  724.   DWORD dwKey;
  725.   IConnectionPoint* pConnPoint;
  726.   if (!m_dwPaperSink)
  727.   {
  728.     // Get the Paper Sink connection point.
  729.     pConnPoint = GetConnectionPoint(IID_IPaperSink);
  730.     if (NULL != pConnPoint)
  731.     {
  732.       // Connect the object in the server to the Paper Sink in this client.
  733.       hr = pConnPoint->Advise(m_pCOPaperSink, &dwKey);
  734.       if (SUCCEEDED(hr))
  735.         m_dwPaperSink = dwKey;
  736.       RELEASE_INTERFACE(pConnPoint);
  737.     }
  738.   }
  739.   return hr;
  740. }
  741. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  742.   Method:   CGuiPaper::DisconnectPaperSink
  743.   Summary:  Disconnect the PaperSink from the server COPaper source.
  744.   Args:     void.
  745.   Returns:  HRESULT
  746.               Standard result code. NOERROR for success.
  747. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  748. HRESULT CGuiPaper::DisconnectPaperSink(void)
  749. {
  750.   HRESULT hr = E_FAIL;
  751.   IConnectionPoint* pConnPoint;
  752.   if (m_dwPaperSink)
  753.   {
  754.     // Get the Paper Sink connection point.
  755.     pConnPoint = GetConnectionPoint(IID_IPaperSink);
  756.     if (NULL != pConnPoint)
  757.     {
  758.       // Disconnect the object in the server from the Paper Sink in
  759.       // this client.
  760.       hr = pConnPoint->Unadvise(m_dwPaperSink);
  761.       if (SUCCEEDED(hr))
  762.         m_dwPaperSink = 0;
  763.       RELEASE_INTERFACE(pConnPoint);
  764.     }
  765.   }
  766.   return hr;
  767. }
  768. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  769.   Method:   CGuiPaper::Load
  770.   Summary:  Load drawing data from the current compound file (local
  771.             or remote).
  772.   Args:     void.
  773.   Returns:  HRESULT
  774.               Standard result code. NOERROR for success.
  775. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  776. HRESULT CGuiPaper::Load(void)
  777. {
  778.   HRESULT hr = E_FAIL;
  779.   HCURSOR hCurWait = LoadCursor(NULL, IDC_WAIT);
  780.   HCURSOR hCurPrev;
  781.   RECT WinRect;
  782.   if (NULL != m_pISharePaper)
  783.   {
  784.     // Change cursor to the hour glass.
  785.     hCurPrev = SetCursor(hCurWait);
  786.     // Ask the COPaper object to load the paper data from its
  787.     // current compound file.
  788.     m_bPainting = TRUE;
  789.     hr = m_pISharePaper->Load(&WinRect);
  790.     m_bPainting = FALSE;
  791.     if (SUCCEEDED(hr))
  792.     {
  793.       if (WinRect.right == m_WinRect.right
  794.           || WinRect.bottom == m_WinRect.bottom)
  795.         PaintWin();
  796.       else
  797.       {
  798.         m_WinRect.right = WinRect.right;
  799.         m_WinRect.bottom = WinRect.bottom;
  800.         hr = ResizeWin(m_WinRect.right, m_WinRect.bottom);
  801.       }
  802.       m_bDirty = FALSE;
  803.     }
  804.     // Set Cursor back to what it was.
  805.     SetCursor(hCurPrev);
  806.   }
  807.   return hr;
  808. }
  809. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  810.   Method:   CGuiPaper::LoadLocal
  811.   Summary:  Load drawing data from the local DCDSERVE server.
  812.   Args:     void.
  813.   Returns:  HRESULT
  814.               Standard result code. NOERROR for success.
  815. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  816. HRESULT CGuiPaper::LoadLocal(void)
  817. {
  818.   HRESULT hr;
  819.   BOOL bFirst;
  820.   // Make sure we unlock the paper object.
  821.   Lock(FALSE);
  822.   // Disconnect all Sinks--currently only one: PaperSink. This
  823.   // officially stops all PaperSink notifications.
  824.   hr = DisconnectPaperSink();
  825.   // Release the main interface pointer copy held in CGuiPaper.
  826.   // This causes destruction (in the server) of the current
  827.   // COPaper object.
  828.   RELEASE_INTERFACE(m_pISharePaper);
  829.   // Call COM service to create a new COPaper instance. We are
  830.   // not aggregating it so we ask for its IShare Paper interface
  831.   // directly.
  832.   hr = CoCreateInstance(
  833.          CLSID_SharePaper,
  834.          NULL,
  835.          CLSCTX_LOCAL_SERVER,
  836.          IID_ISharePaper,
  837.          (PPVOID)&m_pISharePaper);
  838.   if (SUCCEEDED(hr))
  839.   {
  840.     // Init the COPaper object.
  841.     GetClientRect(m_hWnd, &m_WinRect);
  842.     hr = m_pISharePaper->InitPaper(&m_WinRect, &bFirst);
  843.     if (SUCCEEDED(hr))
  844.     {
  845.       // Reconnect all Sinks--currently only one: PaperSink.
  846.       // This restores all PaperSink notifications.
  847.       hr = ConnectPaperSink();
  848.       if (SUCCEEDED(hr))
  849.       {
  850.         if (bFirst)
  851.         {
  852.           // Lock and load.
  853.           Lock(TRUE);
  854.           hr = Load();
  855.         }
  856.         else
  857.         {
  858.           Lock(FALSE);
  859.           // If this is not first init then resize this client's window
  860.           // to match what the COPaper object is using for its size.
  861.           hr = ResizeWin(m_WinRect.right, m_WinRect.bottom);
  862.           PaintWin();
  863.         }
  864.       }
  865.     }
  866.   }
  867.   return hr;
  868. }
  869. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  870.   Method:   CGuiPaper::LoadRemote
  871.   Summary:  Load the shared drawing from a DCDSERVE server on a remote
  872.             machine that the user specifies by name in a dialog.
  873.   Args:     void
  874.   Returns:  HRESULT
  875.               Standard result code. NOERROR for success.
  876. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  877. HRESULT CGuiPaper::LoadRemote(void)
  878. {
  879.   HRESULT hr = NOERROR;
  880.   BOOL bFirst;
  881.   COPaperSink* pCobSink = NULL;
  882.   HCURSOR hCurPrev;
  883.   HCURSOR hCurWait = LoadCursor(NULL, IDC_WAIT);
  884.   COSERVERINFO* pServerInfo = &g_ServerInfo;
  885.   MULTI_QI qiRes;
  886.   int iAns;
  887.   CDlgLoadRemote dlgLoadRemote;
  888.   // Ask user for the remote machine name. Cancel this whole load if
  889.   // he cancels the dialog.
  890.   iAns = dlgLoadRemote.ShowDialog(
  891.            m_hInst,
  892.            MAKEINTRESOURCE(IDD_LOAD_REMOTE),
  893.            m_hWnd);
  894.   // Ensure a remote machine name was specified by user.
  895.   if (IDOK == iAns && pServerInfo->pwszName[0])
  896.   {
  897.     hr = E_FAIL;
  898.     // Unload the existing COPaper object.
  899.     if (m_pISharePaper)
  900.     {
  901.       // Ask User if save is desired.
  902.       AskSave();
  903.       // Change cursor to the hour glass.
  904.       hCurPrev = SetCursor(hCurWait);
  905.       // Clear the window of the previous drawing.
  906.       ClearWin();
  907.       // Make sure we unlock the paper object.
  908.       Lock(FALSE);
  909.       // Disconnect all Sinks--currently only one: PaperSink. This
  910.       // officially stops all PaperSink notifications.
  911.       hr = DisconnectPaperSink();
  912.       // Release the main interface pointer copy held in CGuiPaper.
  913.       // This causes destruction (in the server) of the current
  914.       // COPaper object.
  915.       RELEASE_INTERFACE(m_pISharePaper);
  916.     }
  917.     // Load a new COPaper from the server on a user-specifed remote machine.
  918.     // Call COM service to create an instance of the remote COPaper
  919.     // COM object. We are not aggregating this new object (viz, the
  920.     // NULL parameter), so we ask for its IShareDraw interface directly.
  921.     qiRes.pIID = &IID_ISharePaper;
  922.     qiRes.pItf = NULL;
  923.     qiRes.hr = 0;
  924.     hr = CoCreateInstanceEx(
  925.            CLSID_SharePaper,
  926.            NULL,
  927.            CLSCTX_REMOTE_SERVER,
  928.            pServerInfo,
  929.            1,
  930.            &qiRes);
  931.     if (SUCCEEDED(hr))
  932.     {
  933.       hr = qiRes.hr;
  934.       if (SUCCEEDED(hr))
  935.       {
  936.         // Grab our copy of the returned interface pointer. An AddRef was
  937.         // done by CoCreateInstanceEx on this interface pointer.
  938.         m_pISharePaper = (ISharePaper*)qiRes.pItf;
  939.         // Init the newly created COPaper object.
  940.         GetClientRect(m_hWnd, &m_WinRect);
  941.         hr = m_pISharePaper->InitPaper(&m_WinRect, &bFirst);
  942.         if (SUCCEEDED(hr))
  943.         {
  944.           // Reconnect all Sinks--currently only one: PaperSink.
  945.           // This restores all PaperSink notifications.
  946.           hr = ConnectPaperSink();
  947.           if (SUCCEEDED(hr))
  948.           {
  949.             if (bFirst)
  950.             {
  951.               // Lock and load.
  952.               Lock(TRUE);
  953.               hr = Load();
  954.             }
  955.             else
  956.             {
  957.               Lock(FALSE);
  958.               // If this is not first init then resize this client's
  959.               // window to match what the remote COPaper object is
  960.               // using for its window size.
  961.               hr = ResizeWin(m_WinRect.right, m_WinRect.bottom);
  962.             }
  963.             PaintWin();
  964.           }
  965.         }
  966.       }
  967.     }
  968.     if (FAILED(hr))
  969.     {
  970.       HrMsg(m_hWnd, TEXT(REMOTE_CREATE_ERR_STR), hr);
  971.       // If error with remote load then restore local drawing.
  972.       hr = LoadLocal();
  973.     }
  974.     // Set Cursor back to what it was.
  975.     SetCursor(hCurPrev);
  976.   }
  977.   return hr;
  978. }
  979. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  980.   Method:   CGuiPaper::Save
  981.   Summary:  Calls on COPaper to save the current drawing's paper data in
  982.             its current compound file.
  983.   Args:     void.
  984.   Returns:  HRESULT
  985.               Standard result code. NOERROR for success.
  986. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  987. HRESULT CGuiPaper::Save(void)
  988. {
  989.   HRESULT hr = E_FAIL;
  990.   HCURSOR hCurWait = LoadCursor(NULL, IDC_WAIT);
  991.   HCURSOR hCurPrev;
  992.   if (NULL != m_pISharePaper)
  993.   {
  994.     // Change cursor to the hour glass.
  995.     hCurPrev = SetCursor(hCurWait);
  996.     // Ask the COPaper object to save itself to its current compound file.
  997.     hr = m_pISharePaper->Save();
  998.     if (SUCCEEDED(hr))
  999.       m_bDirty = FALSE;
  1000.     // Set Cursor back to what it was.
  1001.     SetCursor(hCurPrev);
  1002.   }
  1003.   return hr;
  1004. }
  1005. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1006.   Method:   CGuiPaper::AskSave
  1007.   Summary:  Checks dirty flag (ie, if current displayed paper data was
  1008.             modified and is out of sync with the paper data stored in a
  1009.             compound file). If dirty, then ask user in simple dialog if he
  1010.             wants to save new data. If he says yes, then save the current
  1011.             paper data into the current compound file.
  1012.   Args:     void.
  1013.   Returns:  INT
  1014.               Value returned from the Win32 MessageBox function.
  1015. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1016. INT CGuiPaper::AskSave(void)
  1017. {
  1018.   int iAns = IDNO;
  1019.   TCHAR szTitle[MAX_STRING_LENGTH];
  1020.   TCHAR szAsk[MAX_STRING_LENGTH];
  1021.   TCHAR szMsg[MAX_PATH + MAX_STRING_LENGTH];
  1022.   // If current data is dirty then ask user if he wants to save it.
  1023.   if (m_bDirty)
  1024.   {
  1025.     if (LoadString(m_hInst, IDS_DRAWING_CHANGED, szTitle, MAX_STRING_LENGTH)
  1026.         && LoadString(m_hInst, IDS_ASK_SAVE, szAsk, MAX_STRING_LENGTH))
  1027.     {
  1028.       lstrcpy(szMsg, TEXT(MAIN_WINDOW_TITLE_STR));
  1029.       lstrcat(szMsg, szAsk);
  1030.       // Display AskSaveDlg to user. Ask if he wants to save.
  1031.       iAns = MessageBox(
  1032.                m_hWnd,
  1033.                szMsg,
  1034.                szTitle,
  1035.                MB_YESNOCANCEL | MB_ICONEXCLAMATION);
  1036.       if (IDYES == iAns)
  1037.       {
  1038.         // Tell COPaper to save itself to its current compound file.
  1039.         Save();
  1040.       }
  1041.     }
  1042.   }
  1043.   return iAns;
  1044. }
  1045. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1046.   Method:   CGuiPaper::PickColor
  1047.   Summary:  Uses the Choose Color common dialog to ask user for new
  1048.             Pen color. Return that new color.
  1049.   Args:     void.
  1050.   Returns:  COLORREF
  1051.               New chosen color.
  1052. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1053. COLORREF CGuiPaper::PickColor(void)
  1054. {
  1055.   COLORREF crNewColor = m_crInkColor;
  1056.   SHORT i;
  1057.   // Init the custom color array with gray colors.
  1058.   for (i=0; i<16; i++)
  1059.     m_acrCustColors[i] = RGB(i*16, i*16, i*16);
  1060.   // Init the Choose Color structure.
  1061.   m_ChooseColor.lStructSize = sizeof(CHOOSECOLOR);
  1062.   m_ChooseColor.hwndOwner = m_hWnd;
  1063.   m_ChooseColor.hInstance = (HWND) m_hInst;
  1064.   m_ChooseColor.rgbResult = m_crInkColor;
  1065.   m_ChooseColor.lpCustColors = (DWORD*) m_acrCustColors;
  1066.   m_ChooseColor.Flags = CC_PREVENTFULLOPEN | CC_RGBINIT;
  1067.   m_ChooseColor.lCustData = 0L;
  1068.   m_ChooseColor.lpfnHook = NULL;
  1069.   m_ChooseColor.lpTemplateName = NULL;
  1070.   if (ChooseColor(&m_ChooseColor))
  1071.     crNewColor = m_ChooseColor.rgbResult;
  1072.   return crNewColor;
  1073. }