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 / stoclien / stoclien.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-30  |  32.8 KB  |  963 lines

  1. /*+==========================================================================
  2.   File:      STOCLIEN.CPP
  3.  
  4.   Summary:   This client loads and accesses the Connectable DllPaper
  5.              component provided in a separate in-process COM Server
  6.              (STOSERVE built in the sibling STOSERVE directory). Thus to
  7.              run STOCLIEN you must build STOSERVE first. This client
  8.              application is meant to exercise the STOSERVE in-process
  9.              server and to illustrate the use of the compound file storage
  10.              supported by both the CPapFile C++ object in STOCLIEN and the
  11.              COPaper COM object in the STOSERVE server. Both client and
  12.              server cooperate to use compound files to store drawing
  13.              paper data.
  14.  
  15.              The COPaper object maintains logic and data to represent a
  16.              white sheet of free-form drawing paper. The behavior is much
  17.              like other C++ scribble code samples except that the
  18.              STOCLIEN/STOSERVE pair are implemented using custom COM
  19.              objects, interfaces, and standard COM services. The client
  20.              mouse activity is used to draw ink lines on the paper
  21.              surface. The COPaper object keeps a memory array of the Ink
  22.              data but performs no GUI display. This STOCLIEN (largely
  23.              within the CGuiPaper object) manages the display of the
  24.              current drawing data.
  25.  
  26.              There is no GUI behavior in the server--it is all in this
  27.              client.  This client instantiates one instance of the
  28.              server's COPaper COM object and then allows the user to
  29.              use the mouse to draw lines in the client area of the
  30.              main window.
  31.  
  32.              For a comprehensive tutorial code tour of STOCLIEN's contents
  33.              and offerings see the tutorial STOCLIEN.HTM file. For
  34.              more specific technical details on the internal workings see
  35.              the comments dispersed throughout the STOCLIEN source code.
  36.              For more details on the STOSERVE.DLL that STOCLIEN works with
  37.              see the STOSERVE.HTM file in the main tutorial directory.
  38.  
  39.   Classes:   CMainWindow.
  40.  
  41.   Functions: InitApplication, WinMain
  42.  
  43.   Origin:    6-10-96: atrent - Editor-inheritance from the CONCLIEN source.
  44.  
  45. ----------------------------------------------------------------------------
  46.   This file is part of the Microsoft COM Tutorial Code Samples.
  47.  
  48.   Copyright (C) Microsoft Corporation, 1997.  All rights reserved.
  49.  
  50.   This source code is intended only as a supplement to Microsoft
  51.   Development Tools and/or on-line documentation.  See these other
  52.   materials for detailed information regarding Microsoft code samples.
  53.  
  54.   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  55.   KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  56.   IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  57.   PARTICULAR PURPOSE.
  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 INITGUID.H only once (here) in the entire app because we
  65.     will be defining GUIDs and want them as constants in the data segment.
  66.   We include COMMDLG.H because we will be using the Open File
  67.     and potentially other Common dialogs.
  68.   We include APPUTIL.H because we will be building this application using
  69.     the convenient Virtual Window and Dialog classes and other
  70.     utility functions in the APPUTIL Library (ie, APPUTIL.LIB).
  71.   We include IPAPER.H and PAPGUIDS.H for the common paper-related Interface
  72.     class, GUID, and CLSID specifications.
  73.   We include PAPFILE.H because it has the C++ class used for compound file
  74.     storage of drawing paper data.
  75.   We include GUIPAPER.H because it has the C++ class used for GUI display
  76.     of the drawing paper.
  77.   We include STOCLIEN.H because it has class and resource definitions
  78.     specific to this STOCLIEN application.
  79. ---------------------------------------------------------------------------*/
  80. #include <windows.h>
  81. #include <ole2.h>
  82. #include <olectl.h>
  83. #include <initguid.h>
  84. #include <commdlg.h>
  85. #include <apputil.h>
  86. #include <ipaper.h>
  87. #include <papguids.h>
  88. #include "papfile.h"
  89. #include "guipaper.h"
  90. #include "stoclien.h"
  91.  
  92.  
  93. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  94.   Method:   CMainWindow::CMainWindow
  95.  
  96.   Summary:  CMainWindow Constructor.
  97.  
  98.   Args:     .
  99.  
  100.   Returns:  .
  101. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  102. CMainWindow::CMainWindow()
  103. {
  104.   // Ensure these member variable strings are null strings.
  105.   m_szFileName[0] = 0;
  106.   m_szFileTitle[0] = 0;
  107.  
  108.   // Fill in the Open File Name Common Dialog's OPENFILENAME structure.
  109.   m_ofnFile.lStructSize = sizeof(OPENFILENAME);
  110.   m_ofnFile.hwndOwner = m_hWnd;
  111.   m_ofnFile.hInstance = m_hInst;
  112.   m_ofnFile.lpstrFilter = TEXT(OFN_DEFAULTFILES_STR);
  113.   m_ofnFile.lpstrCustomFilter = NULL;
  114.   m_ofnFile.nMaxCustFilter = 0;
  115.   m_ofnFile.nFilterIndex = 1;
  116.   m_ofnFile.lpstrFile = m_szFileName;
  117.   m_ofnFile.nMaxFile = MAX_PATH;
  118.   m_ofnFile.lpstrInitialDir = TEXT(".");
  119.   m_ofnFile.lpstrFileTitle = m_szFileTitle;
  120.   m_ofnFile.nMaxFileTitle = MAX_PATH;
  121.   m_ofnFile.lpstrTitle = TEXT(OFN_DEFAULTTITLE_STR);
  122.   m_ofnFile.lpstrDefExt = NULL;
  123.   m_ofnFile.Flags = OFN_HIDEREADONLY;
  124.  
  125.   m_pMsgBox  = NULL;
  126.   m_pGuiPaper = NULL;
  127. }
  128.  
  129.  
  130. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  131.   Method:   CMainWindow::~CMainWindow
  132.  
  133.   Summary:  CMainWindow Destructor.  Destruction of the main window
  134.             indicates that the application should quit and thus the
  135.             PostQuitMessage API is called.
  136.  
  137.   Args:     .
  138.  
  139.   Returns:  .
  140. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  141. CMainWindow::~CMainWindow()
  142. {
  143.   // CMainWindow is derived from CVirWindow which traps the WM_DESTROY
  144.   // message and causes a delete of CMainWindow which in turn causes this
  145.   // destructor to run. The WM_DESTROY results when the window is destoyed
  146.   // after a close of the window. Prior to exiting the main message loop:
  147.  
  148.   // We delete the CGuiPaper and CMsgBox objects that were made in
  149.   // Initinstance.
  150.   DELETE_POINTER(m_pGuiPaper);
  151.   DELETE_POINTER(m_pMsgBox);
  152.  
  153.   // We then post a WM_QUIT message to cause an exit of the main
  154.   // message loop and an exit of this instance of the application.
  155.   PostQuitMessage(0);
  156. }
  157.  
  158.  
  159. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  160.   Method:   CMainWindow::InitInstance
  161.  
  162.   Summary:  Instantiates an instance of the main application window.
  163.             This method must be called only once, immediately after
  164.             window class construction.  We take care to delete 'this'
  165.             CMainWindow if we must return the error condition FALSE.
  166.  
  167.   Args:     HINSTANCE hInstance,
  168.               Handle of the application instance.
  169.             LPSTR pszCmdLine,
  170.               Pointer to the application's invocation command line.
  171.             int nCmdShow)
  172.               Command to pass to ShowWindow.
  173.  
  174.   Modifies: m_szHelpFile, m_pMsgBox.
  175.  
  176.   Returns:  BOOL.
  177.               TRUE if succeeded.
  178.               FALSE if failed.
  179. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  180. BOOL CMainWindow::InitInstance(
  181.        HINSTANCE hInstance,
  182.        LPSTR pszCmdLine,
  183.        int nCmdShow)
  184. {
  185.   BOOL bOk = FALSE;
  186.   HWND hWnd = NULL;
  187.   HRESULT hr = E_FAIL;
  188.  
  189.   // Create the Message Box and Message Log objects.
  190.   m_pMsgBox = new CMsgBox;
  191.  
  192.   // Create the CGuiPaper C++ object.
  193.   m_pGuiPaper = new CGuiPaper;
  194.  
  195.   if (NULL != m_pMsgBox && NULL != m_pGuiPaper)
  196.   {
  197.     // Note, the Create method sets the m_hWnd member so we don't
  198.     // need to set it explicitly here first. Here is the create of this
  199.     // window.  Size the window reasonably. Create sets both m_hInst and
  200.     // m_hWnd. This creates the main client window.
  201.     hWnd = Create(
  202.              TEXT(MAIN_WINDOW_CLASS_NAME_STR),
  203.              TEXT(MAIN_WINDOW_TITLE_STR),
  204.              WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX
  205.                | WS_MAXIMIZEBOX | WS_THICKFRAME,
  206.              CW_USEDEFAULT,
  207.              CW_USEDEFAULT,
  208.              ::GetSystemMetrics(SM_CXSCREEN)*2/5,
  209.              ::GetSystemMetrics(SM_CYSCREEN)*2/5,
  210.              NULL,
  211.              NULL,
  212.              hInstance);
  213.     if (NULL != hWnd)
  214.     {
  215.       if (pszCmdLine[0] == 0)
  216.         pszCmdLine = NULL;
  217.  
  218. #if defined(UNICODE)
  219.       {
  220.         WCHAR wszUc[MAX_PATH];
  221.  
  222.         if (pszCmdLine)
  223.         {
  224.           // Convert Ansi command line string to Unicode if we are
  225.           // compiled for Unicode.
  226.           AnsiToUc(pszCmdLine, wszUc, MAX_PATH);
  227.           // Init the new GuiPaper.
  228.           bOk = m_pGuiPaper->Init(m_hInst, m_hWnd, wszUc);
  229.         }
  230.         else
  231.           bOk = m_pGuiPaper->Init(m_hInst, m_hWnd, NULL);
  232.       }
  233. #else
  234.       // Init the new GuiPaper.
  235.       bOk = m_pGuiPaper->Init(m_hInst, m_hWnd, pszCmdLine);
  236. #endif
  237.  
  238.       if (bOk)
  239.       {
  240.         // Ensure the new window is shown on screen and content
  241.         // is painted.
  242.         ::ShowWindow(m_hWnd, nCmdShow);
  243.         ::UpdateWindow(m_hWnd);
  244.  
  245.         // Build a path to where the help file should be (it should be in
  246.         // the same directory as the .EXE but with the .HTM extension).
  247.         MakeFamilyPath(hInstance, m_szHelpFile, TEXT(HELP_FILE_EXT));
  248.  
  249.         // Init the Message Box object.
  250.         bOk = m_pMsgBox->Init(m_hInst, m_hWnd);
  251.  
  252.         // Lock the paper object for drawing by this client. Can't draw
  253.         // with mouse until doing this. Set the proper checkmark
  254.         // indicator on the menu selections as well.
  255.         hr = m_pGuiPaper->DrawOn();
  256.         if (SUCCEEDED(hr))
  257.         {
  258.           HMENU hMenu  = ::GetMenu(m_hWnd);
  259.  
  260.           ::CheckMenuItem(
  261.               hMenu,
  262.               IDM_DRAW_ON,
  263.               MF_BYCOMMAND | MF_CHECKED);
  264.           ::CheckMenuItem(
  265.               hMenu,
  266.               IDM_DRAW_OFF,
  267.               MF_BYCOMMAND | MF_UNCHECKED);
  268.  
  269.           // Connect the new Paper Sink in this client to receive event
  270.           // calls from a COPaper source in the server.
  271.           hr = m_pGuiPaper->ConnectPaperSink();
  272.           if (SUCCEEDED(hr))
  273.           {
  274.             ::CheckMenuItem(
  275.                 hMenu,
  276.                 IDM_SINK_ON,
  277.                 MF_BYCOMMAND | MF_CHECKED);
  278.             ::CheckMenuItem(
  279.                 hMenu,
  280.                 IDM_SINK_OFF,
  281.                 MF_BYCOMMAND | MF_UNCHECKED);
  282.  
  283.             // Try to load the paper data compound file. Either the
  284.             // default STOCLIEN.PAP or the one specified on the
  285.             // invocation command line.
  286.             m_pGuiPaper->Load();
  287.           }
  288.         }
  289.       }
  290.     }
  291.   }
  292.  
  293.   if (!bOk)
  294.   {
  295.     DELETE_POINTER(m_pMsgBox);
  296.     DELETE_POINTER(m_pGuiPaper);
  297.   }
  298.  
  299.   bOk = SUCCEEDED(hr);
  300.  
  301.   return (bOk);
  302. }
  303.  
  304.  
  305. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  306.   Method:   CMainWindow::DoMenu
  307.  
  308.   Summary:  Dispatch and handle the main menu commands.
  309.  
  310.   Args:     WPARAM wParam,
  311.               First message parameter (word sized).
  312.             LPARAM lParam)
  313.               Second message parameter (long sized).
  314.  
  315.   Modifies: m_ofnFile, ...
  316.  
  317.   Returns:  LRESULT
  318.               Standard Windows WindowProc return value.
  319. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  320. LRESULT CMainWindow::DoMenu(
  321.           WPARAM wParam,
  322.           LPARAM lParam)
  323. {
  324.   LRESULT lResult = FALSE;
  325.   HMENU hMenu  = ::GetMenu(m_hWnd);
  326.  
  327.   switch (LOWORD(wParam))
  328.   {
  329.     //----------------------------------------------------------------------
  330.     // Handle File Menu Commands.
  331.     //----------------------------------------------------------------------
  332.     case IDM_FILE_OPEN:
  333.       // Open existing compound file and ask COPaper to load itself from it.
  334.       m_pGuiPaper->Open();
  335.       break;
  336.     case IDM_FILE_SAVE:
  337.       // Open current compound file and ask COPaper to save itself to it.
  338.       m_pGuiPaper->Save();
  339.       break;
  340.     case IDM_FILE_SAVEAS:
  341.       // Create new compound file and ask COPaper to save itself to it.
  342.       m_pGuiPaper->SaveAs();
  343.       break;
  344.     case IDM_FILE_EXIT:
  345.       // The user commands us to exit this application so we tell the
  346.       // Main window to close itself.
  347.       ::PostMessage(m_hWnd, WM_CLOSE, 0, 0);
  348.       break;
  349.  
  350.     //----------------------------------------------------------------------
  351.     // Handle Draw Menu Choice Commands.
  352.     //----------------------------------------------------------------------
  353.     case IDM_DRAW_ON:
  354.       // Lock the paper object for drawing by this client. Can't draw
  355.       // with mouse until doing this. Set the proper checkmark indicator
  356.       // on the menu selections as well.
  357.       {
  358.         HRESULT hr;
  359.         HMENU hMenu  = ::GetMenu(m_hWnd);
  360.         BOOL bDrawOn = ::GetMenuState(
  361.                            hMenu,
  362.                            IDM_DRAW_ON,
  363.                            MF_BYCOMMAND) & MF_CHECKED;
  364.         if (!bDrawOn)
  365.         {
  366.           hr = m_pGuiPaper->DrawOn();
  367.           if (SUCCEEDED(hr))
  368.           {
  369.             ::CheckMenuItem(
  370.                 hMenu,
  371.                 IDM_DRAW_ON,
  372.                 MF_BYCOMMAND | MF_CHECKED);
  373.             ::CheckMenuItem(
  374.                 hMenu,
  375.                 IDM_DRAW_OFF,
  376.                 MF_BYCOMMAND | MF_UNCHECKED);
  377.             // Ask Paper object to redraw its current content.
  378.             m_pGuiPaper->PaintWin();
  379.           }
  380.         }
  381.       }
  382.       break;
  383.     case IDM_DRAW_OFF:
  384.       // Unlock the paper object for drawing by this client. Set the
  385.       // proper checkmark indicator on the menu selections as well.
  386.       {
  387.         HRESULT hr;
  388.         HMENU hMenu  = ::GetMenu(m_hWnd);
  389.         BOOL bDrawOff = ::GetMenuState(
  390.                             hMenu,
  391.                             IDM_DRAW_OFF,
  392.                             MF_BYCOMMAND) & MF_CHECKED;
  393.         if (!bDrawOff)
  394.         {
  395.           hr = m_pGuiPaper->DrawOff();
  396.           if (SUCCEEDED(hr))
  397.           {
  398.             ::CheckMenuItem(
  399.                 hMenu,
  400.                 IDM_DRAW_ON,
  401.                 MF_BYCOMMAND | MF_UNCHECKED);
  402.             ::CheckMenuItem(
  403.                 hMenu,
  404.                 IDM_DRAW_OFF,
  405.                 MF_BYCOMMAND | MF_CHECKED);
  406.           }
  407.         }
  408.       }
  409.       break;
  410.     case IDM_DRAW_REDRAW:
  411.       // Ask Paper object to redraw its current content.
  412.       m_pGuiPaper->PaintWin();
  413.       break;
  414.     case IDM_DRAW_ERASE:
  415.       // Ask Paper object to erase its current content and if it can
  416.       // Clear drawing display window.
  417.       m_pGuiPaper->Erase();
  418.       break;
  419.  
  420.     //----------------------------------------------------------------------
  421.     // Handle Pen Menu Commands.
  422.     //----------------------------------------------------------------------
  423.     case IDM_PEN_COLOR:
  424.       // Select Ink drawing color using ChooseColor common dialog.
  425.       {
  426.         COLORREF crNewColor = m_pGuiPaper->PickColor();
  427.         m_pGuiPaper->InkColor(crNewColor);
  428.       }
  429.       break;
  430.     case IDM_PEN_THIN:
  431.       // Select thin Ink width. Set menu check indicator.
  432.       {
  433.         HRESULT hr;
  434.         HMENU hMenu  = ::GetMenu(m_hWnd);
  435.         BOOL bOn = ::GetMenuState(
  436.                        hMenu,
  437.                        IDM_PEN_THIN,
  438.                        MF_BYCOMMAND) & MF_CHECKED;
  439.         if (!bOn)
  440.         {
  441.           hr = m_pGuiPaper->InkWidth(INK_THIN);
  442.           if (SUCCEEDED(hr))
  443.           {
  444.             ::CheckMenuItem(
  445.                 hMenu,
  446.                 IDM_PEN_THIN,
  447.                 MF_BYCOMMAND | MF_CHECKED);
  448.             ::CheckMenuItem(
  449.                 hMenu,
  450.                 IDM_PEN_MEDIUM,
  451.                 MF_BYCOMMAND | MF_UNCHECKED);
  452.             ::CheckMenuItem(
  453.                 hMenu,
  454.                 IDM_PEN_THICK,
  455.                 MF_BYCOMMAND | MF_UNCHECKED);
  456.           }
  457.         }
  458.       }
  459.       break;
  460.     case IDM_PEN_MEDIUM:
  461.       // Select medium Ink width. Set menu check indicator.
  462.       {
  463.         HRESULT hr;
  464.         HMENU hMenu  = ::GetMenu(m_hWnd);
  465.         BOOL bOn = ::GetMenuState(
  466.                        hMenu,
  467.                        IDM_PEN_MEDIUM,
  468.                        MF_BYCOMMAND) & MF_CHECKED;
  469.         if (!bOn)
  470.         {
  471.           hr = m_pGuiPaper->InkWidth(INK_MEDIUM);
  472.           if (SUCCEEDED(hr))
  473.           {
  474.             ::CheckMenuItem(
  475.                 hMenu,
  476.                 IDM_PEN_MEDIUM,
  477.                 MF_BYCOMMAND | MF_CHECKED);
  478.             ::CheckMenuItem(
  479.                 hMenu,
  480.                 IDM_PEN_THICK,
  481.                 MF_BYCOMMAND | MF_UNCHECKED);
  482.             ::CheckMenuItem(
  483.                 hMenu,
  484.                 IDM_PEN_THIN,
  485.                 MF_BYCOMMAND | MF_UNCHECKED);
  486.           }
  487.         }
  488.       }
  489.       break;
  490.     case IDM_PEN_THICK:
  491.       // Select thick Ink width. Set menu check indicator.
  492.       {
  493.         HRESULT hr;
  494.         HMENU hMenu  = ::GetMenu(m_hWnd);
  495.         BOOL bOn = ::GetMenuState(
  496.                        hMenu,
  497.                        IDM_PEN_THICK,
  498.                        MF_BYCOMMAND) & MF_CHECKED;
  499.         if (!bOn)
  500.         {
  501.           hr = m_pGuiPaper->InkWidth(INK_THICK);
  502.           if (SUCCEEDED(hr))
  503.           {
  504.             ::CheckMenuItem(
  505.                 hMenu,
  506.                 IDM_PEN_THICK,
  507.                 MF_BYCOMMAND | MF_CHECKED);
  508.             ::CheckMenuItem(
  509.                 hMenu,
  510.                 IDM_PEN_MEDIUM,
  511.                 MF_BYCOMMAND | MF_UNCHECKED);
  512.             ::CheckMenuItem(
  513.                 hMenu,
  514.                 IDM_PEN_THIN,
  515.                 MF_BYCOMMAND | MF_UNCHECKED);
  516.           }
  517.         }
  518.       }
  519.       break;
  520.  
  521.     //----------------------------------------------------------------------
  522.     // Handle Sink Menu Commands.
  523.     //----------------------------------------------------------------------
  524.     case IDM_SINK_ON:
  525.       // Connect the client's event Paper Sink to the COPaper event
  526.       // source in the server. Set the proper checkmark indicator on
  527.       // the menu selections as well.
  528.       {
  529.         HRESULT hr;
  530.         HMENU hMenu  = ::GetMenu(m_hWnd);
  531.         BOOL bConnected = ::GetMenuState(
  532.                               hMenu,
  533.                               IDM_SINK_ON,
  534.                               MF_BYCOMMAND) & MF_CHECKED;
  535.         if (!bConnected)
  536.         {
  537.           hr = m_pGuiPaper->ConnectPaperSink();
  538.           if (SUCCEEDED(hr))
  539.           {
  540.             ::CheckMenuItem(
  541.                 hMenu,
  542.                 IDM_SINK_ON,
  543.                 MF_BYCOMMAND | MF_CHECKED);
  544.             ::CheckMenuItem(
  545.                 hMenu,
  546.                 IDM_SINK_OFF,
  547.                 MF_BYCOMMAND | MF_UNCHECKED);
  548.           }
  549.         }
  550.       }
  551.       break;
  552.     case IDM_SINK_OFF:
  553.       // Disconnect the client's event Paper Sink from the COPaper
  554.       // event source in the server. Set the proper checkmark indicator
  555.       // on the menu selections as well.
  556.       {
  557.         HRESULT hr;
  558.         HMENU hMenu  = ::GetMenu(m_hWnd);
  559.         BOOL bDisConnected = ::GetMenuState(
  560.                                 hMenu,
  561.                                 IDM_SINK_OFF,
  562.                                 MF_BYCOMMAND) & MF_CHECKED;
  563.         if (!bDisConnected)
  564.         {
  565.           hr = m_pGuiPaper->DisconnectPaperSink();
  566.           if (SUCCEEDED(hr))
  567.           {
  568.             ::CheckMenuItem(
  569.                 hMenu,
  570.                 IDM_SINK_ON,
  571.                 MF_BYCOMMAND | MF_UNCHECKED);
  572.             ::CheckMenuItem(
  573.                 hMenu,
  574.                 IDM_SINK_OFF,
  575.                 MF_BYCOMMAND | MF_CHECKED);
  576.           }
  577.         }
  578.       }
  579.       break;
  580.  
  581.     //----------------------------------------------------------------------
  582.     // Handle Help Menu Commands.
  583.     //----------------------------------------------------------------------
  584.     case IDM_HELP_CONTENTS:
  585.       // We have some stubbed support here for bringing up the online
  586.       // Help for this application.
  587.       ReadHelp(m_hWnd, m_szHelpFile);
  588.       break;
  589.     case IDM_HELP_TUTORIAL:
  590.       // Call the APPUTIL utility function, ReadTutorial, to Browse the HTML
  591.       // tutorial narrative file associated with this tutorial code sample.
  592.       ReadTutorial(m_hInst, m_hWnd, TEXT(HTML_FILE_EXT));
  593.       break;
  594.     case IDM_HELP_TUTSERVER:
  595.       // Call the APPUTIL utility function, ReadTutorial, to Browse the HTML
  596.       // tutorial narrative file associated with the STOSERVE COM server.
  597.       ReadTutorial(m_hInst, m_hWnd, TEXT(SERVER_TUTFILE_STR));
  598.       break;
  599.     case IDM_HELP_READSOURCE:
  600.       // Call the APPUTIL utility function ReadSource to allow the
  601.       // user to open and read any of the source files of STOCLIEN.
  602.       ReadSource(m_hWnd, &m_ofnFile);
  603.       break;
  604.     case IDM_HELP_ABOUT:
  605.       {
  606.         CAboutBox dlgAboutBox;
  607.  
  608.         // Show the standard About Box dialog for this EXE by telling the
  609.         // dialog C++ object to show itself by invoking its ShowDialog
  610.         // method.  Pass it this EXE instance and the parent window handle.
  611.         // Use a dialog resource ID for the dialog template stored in
  612.         // this EXE module's resources.
  613.         dlgAboutBox.ShowDialog(
  614.           m_hInst,
  615.           MAKEINTRESOURCE(IDM_HELP_ABOUT),
  616.           m_hWnd);
  617.       }
  618.       break;
  619.  
  620.     default:
  621.       // Defer all messages NOT handled here to the Default Window Proc.
  622.       lResult = ::DefWindowProc(m_hWnd, WM_COMMAND, wParam, lParam);
  623.       break;
  624.   }
  625.  
  626.   return(lResult);
  627. }
  628.  
  629.  
  630. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  631.   Method:   CMainWindow::WindowProc
  632.  
  633.   Summary:  Main window procedure for this window object.  See CVirWindow
  634.             in the APPUTIL library (APPUTIL.CPP) for details on how this
  635.             method gets called by the global WindowProc.
  636.  
  637.   Args:     UINT uMsg,
  638.               Windows message that is "sent" to this window.
  639.             WPARAM wParam,
  640.               First message parameter (word sized).
  641.             LPARAM lParam)
  642.               Second message parameter (long sized).
  643.  
  644.   Modifies: ...
  645.  
  646.   Returns:  LRESULT
  647.               Standard Windows WindowProc return value.
  648. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  649. LRESULT CMainWindow::WindowProc(
  650.           UINT uMsg,
  651.           WPARAM wParam,
  652.           LPARAM lParam)
  653. {
  654.   LRESULT lResult = FALSE;
  655.  
  656.   switch (uMsg)
  657.   {
  658.     case WM_CREATE:
  659.       break;
  660.  
  661.     case WM_ACTIVATE:
  662.       // If we were newly activated by a mouse click then don't just sit
  663.       // there--re-paint. This is needed when another window was topped
  664.       // over part of STOCLIEN and the user then topped STOCLIEN using
  665.       // a mouse click on the visible part of STOCLIEN. In any case let
  666.       // the windows default WindowProc handle this message to set
  667.       // keyboard focus to the newly active window.
  668.       if (WA_CLICKACTIVE == LOWORD(wParam))
  669.         m_pGuiPaper->PaintWin();
  670.       lResult = ::DefWindowProc(m_hWnd, uMsg, wParam, lParam);
  671.       break;
  672.  
  673.     case WM_MEASUREITEM:
  674.       // Get setup for painting text in this window. For later evolution.
  675.       {
  676.         LPMEASUREITEMSTRUCT lpmis = (LPMEASUREITEMSTRUCT) lParam;
  677.         lpmis->itemHeight = m_tm.tmHeight + m_tm.tmExternalLeading;
  678.         lpmis->itemWidth = m_wWidth;
  679.         lResult = TRUE;
  680.       }
  681.  
  682.     case WM_SIZE:
  683.       // Handle a resize of this window.
  684.       m_wWidth = LOWORD(lParam);
  685.       m_wHeight = HIWORD(lParam);
  686.       // Inform CGuiPaper of the change.
  687.       m_pGuiPaper->Resize(m_wWidth, m_wHeight);
  688.       break;
  689.  
  690.     case WM_PAINT:
  691.       // If something major happened repaint the whole window.
  692.       {
  693.         PAINTSTRUCT ps;
  694.  
  695.         if(BeginPaint(m_hWnd, &ps))
  696.         {
  697.           m_pGuiPaper->PaintWin();
  698.           EndPaint(m_hWnd, &ps);
  699.         }
  700.       }
  701.       break;
  702.  
  703.     case WM_LBUTTONDOWN:
  704.       // Start sequence of ink drawing to the paper.
  705.       m_pGuiPaper->InkStart(LOWORD(lParam), HIWORD(lParam));
  706.       break;
  707.  
  708.     case WM_MOUSEMOVE:
  709.       // Draw inking sequence data.
  710.       m_pGuiPaper->InkDraw(LOWORD(lParam), HIWORD(lParam));
  711.       break;
  712.  
  713.     case WM_LBUTTONUP:
  714.       // Stop an ink drawing sequence.
  715.       m_pGuiPaper->InkStop(LOWORD(lParam), HIWORD(lParam));
  716.       break;
  717.  
  718.     case WM_COMMAND:
  719.       // Dispatch and handle any Menu command messages received.
  720.       lResult = DoMenu(wParam, lParam);
  721.       break;
  722.  
  723.     case WM_CHAR:
  724.       if (wParam == 0x1b)
  725.       {
  726.         // Exit this app if user hits ESC key.
  727.         ::PostMessage(m_hWnd, WM_CLOSE, 0, 0);
  728.       }
  729.       break;
  730.  
  731.     case WM_CLOSE:
  732.       // The user selected Close on the main window's System menu
  733.       // or Exit on the File menu.
  734.       // If there is ink data that has not been saved then ask user
  735.       // if it should be saved. If user cancels then cancel the exit.
  736.       if (IDCANCEL == m_pGuiPaper->AskSave())
  737.         break;
  738.     case WM_QUIT:
  739.       // If the app is being quit then close any associated help windows.
  740.     default:
  741.       // Defer all messages NOT handled above to the Default Window Proc.
  742.       lResult = ::DefWindowProc(m_hWnd, uMsg, wParam, lParam);
  743.       break;
  744.   }
  745.  
  746.   return(lResult);
  747. }
  748.  
  749.  
  750. /*F+F++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  751.   Function: UnicodeOk
  752.  
  753.   Summary:  Checks if the platform will handle unicode versions of
  754.             Win32 string API calls.
  755.  
  756.   Args:     void
  757.  
  758.   Returns:  BOOL
  759.               TRUE if unicode support; FALSE if not.
  760. ------------------------------------------------------------------------F-F*/
  761. BOOL UnicodeOk(void)
  762. {
  763.   BOOL bOk = TRUE;
  764.   TCHAR szUserName[MAX_STRING_LENGTH];
  765.   DWORD dwSize = MAX_STRING_LENGTH;
  766.  
  767.   if (!GetUserName(szUserName, &dwSize))
  768.     bOk = ERROR_CALL_NOT_IMPLEMENTED == GetLastError() ? FALSE : TRUE;
  769.  
  770.   return bOk;
  771. }
  772.  
  773.  
  774. /*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
  775.   Function: InitApplication
  776.  
  777.   Summary:  Initializes the application and registers its main window
  778.             class. InitApplication is called only once (in WinMain).
  779.  
  780.   Args:     HINSTANCE hInstance)
  781.               Handle to the first instance of the application.
  782.  
  783.   Returns:  BOOL.
  784.               TRUE if success.
  785.               FALSE if fail.
  786. F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
  787. BOOL InitApplication(
  788.        HINSTANCE hInstance)
  789. {
  790.   BOOL bOk;
  791.   // The window class for all instances of the main frame window.
  792.   WNDCLASSEX wcf;
  793.  
  794.   // Assign the appropriate values for this main frame window class.
  795.   wcf.cbSize        = sizeof(WNDCLASSEX);
  796.   wcf.cbClsExtra    = 0;            // No per-class extra data.
  797.   wcf.cbWndExtra    = 0;            // No per-window extra data.
  798.   wcf.hInstance     = hInstance;    // Application module instance.
  799.   wcf.lpfnWndProc   = &WindowProc;  // Global Window Procedure (defined in
  800.                                     // APPUTIL for all CVirWindows).
  801.   wcf.hCursor       = LoadCursor(                  // Load app cursor.
  802.                         hInstance,
  803.                         TEXT("AppCursor"));
  804.   wcf.hIcon         = (HICON) LoadIcon(            // Load app icon.
  805.                                 hInstance,
  806.                                 TEXT("AppIcon"));
  807.   wcf.hIconSm       = (HICON) LoadImage(           // Load small icon.
  808.                                 hInstance,
  809.                                 TEXT("AppIcon"),
  810.                                 IMAGE_ICON,
  811.                                 16, 16,
  812.                                 0);
  813.   wcf.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); // Backgnd color.
  814.   wcf.style         = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // Class style(s).
  815.   wcf.lpszClassName = TEXT(MAIN_WINDOW_CLASS_NAME_STR);   // Class name.
  816.   wcf.lpszMenuName  = TEXT(MAIN_WINDOW_CLASS_MENU_STR);   // Menu name.
  817.  
  818.   // Register the window class and return FALSE if unsuccesful.
  819.   bOk = RegisterClassEx(&wcf);
  820.  
  821.   return (bOk);
  822. }
  823.  
  824.  
  825. /*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
  826.   Function: WinMain
  827.  
  828.   Summary:  The Windows main entry point function for this application.
  829.             Initializes the application, the COM Libraries, and starts
  830.             the main application message loop.
  831.  
  832.   Args:     HINSTANCE hInstance,
  833.               Instance handle; a new one for each invocation of this app.
  834.             HINSTANCE hPrevInstance,
  835.               Instance handle of the previous instance. NULL in Win32.
  836.             LPSTR pszCmdLine,
  837.               Windows passes a pointer to the application's
  838.               invocation command line.
  839.             int nCmdShow)
  840.               Bits telling the show state of the application.
  841.  
  842.   Returns:  int
  843.               msg.wParam (upon exit of message loop).
  844.               FALSE if this instance couldn't initialize and run.
  845. F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
  846. extern "C" int PASCAL WinMain(
  847.                         HINSTANCE hInstance,
  848.                         HINSTANCE hPrevInstance,
  849.                         LPSTR pszCmdLine,
  850.                         int nCmdShow)
  851. {
  852.   CMainWindow* pWin = NULL;
  853.   MSG msg;
  854.   HACCEL hAccel;
  855.   int iRun = FALSE;
  856.  
  857.   // If we were compiled for UNICODE and the platform seems OK with this
  858.   // then proceed.  Else we error and exit the app.
  859.   if (UnicodeOk())
  860.   {
  861.     // Call to initialize the COM Library.  Use the SUCCEEDED macro
  862.     // to detect success.  If fail then exit app with error message.
  863.     // Tell COM that this client process and all subordinate threads
  864.     // will live in an apartment threaded world.
  865.     if (SUCCEEDED(CoInitialize(NULL)))
  866.     {
  867.       // If we succeeded in initializing the COM Library we proceed to
  868.       // initialize the application.  If we can't init the application
  869.       // then we signal shut down with an error message exit.
  870.       iRun = InitApplication(hInstance);
  871.       if (iRun)
  872.       {
  873.         // Assume we'll set iRun to TRUE when initialization is done.
  874.         iRun = FALSE;
  875.         // We are still go for running so we try to create a nifty new
  876.         // CMainWindow object for this app instance.
  877.         pWin = new CMainWindow;
  878.         if (NULL != pWin)
  879.         {
  880.           // Now we initialize an instance of the new CMainWindow.
  881.           // This includes creating the main window.
  882.           if (pWin->InitInstance(hInstance, pszCmdLine, nCmdShow))
  883.           {
  884.             // Load the keyboard accelerators from the resources.
  885.             hAccel = LoadAccelerators(hInstance, TEXT("AppAccel"));
  886.             if (NULL != hAccel)
  887.             {
  888.               // Signal App Initialization is successfully done.
  889.               iRun = TRUE;
  890.             }
  891.           }
  892.         }
  893.       }
  894.  
  895.       if (iRun)
  896.       {
  897.         // If we initialized the app instance properly then we are still
  898.         // go for running.  We then start up the main message pump for
  899.         // the application.
  900.         while (GetMessage(&msg, NULL, 0, 0))
  901.         {
  902.           if (!TranslateAccelerator(pWin->GetHwnd(), hAccel, &msg))
  903.           {
  904.             TranslateMessage(&msg);
  905.             DispatchMessage(&msg);
  906.           }
  907.         }
  908.  
  909.         // We also ask COM to unload any unused COM Servers, including our
  910.         // friend, STOSERVE.
  911.         CoFreeUnusedLibraries();
  912.  
  913.         // We'll pass to Windows the reason why we exited the message loop.
  914.         iRun = msg.wParam;
  915.       }
  916.       else
  917.       {
  918.         // We failed to initialize the application. Put up error message
  919.         // box saying that application couldn't be initialized.  Parent
  920.         // window is desktop (ie, NULL). Exit the failed application
  921.         // (ie, by returning FALSE to WinMain).
  922.         ErrorBox(hInstance, NULL, IDS_APPINITFAILED);
  923.  
  924.         // Delete the CMainWindow object.
  925.         DELETE_POINTER(pWin);
  926.       }
  927.  
  928.       // We're exiting this app (either normally or by init failure) so
  929.       // shut down the COM Library.
  930.       CoUninitialize();
  931.     }
  932.     else
  933.     {
  934.       // We failed to Initialize the COM Library. Put up error message
  935.       // box saying that COM Library couldn't be initialized.  Parent
  936.       // window is desktop (ie, NULL). Exit the failed application
  937.       // (ie, by returning FALSE to WinMain).
  938.       ErrorBox(hInstance, NULL, IDS_COMINITFAILED);
  939.     }
  940.   }
  941.   else
  942.   {
  943.     // If we were compiled for UNICODE but the platform has problems with
  944.     // this then indicate an error and exit the app immediately.
  945.     CHAR szMsg[MAX_STRING_LENGTH];
  946.  
  947.     if (LoadStringA(
  948.           hInstance,
  949.           IDS_NOUNICODE,
  950.           szMsg,
  951.           MAX_STRING_LENGTH))
  952.     {
  953.       MessageBoxA(
  954.         NULL,
  955.         szMsg,
  956.         ERROR_TITLE_STR,
  957.         MB_OK | MB_ICONEXCLAMATION);
  958.     }
  959.   }
  960.  
  961.   return iRun;
  962. }
  963.