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

  1. /*+==========================================================================
  2.   File:      LICCLIEN.CPP
  3.  
  4.   Summary:   Based largely on the DLLCLIEN.EXE application code, this
  5.              module is meant to use COM to load and access some COM
  6.              components in two separate DLL COM Servers (DLLSERVE built in
  7.              the sibling DLLSERVE directory and LICSERVE built in the
  8.              sibline LICSERVE directory).  Thus to run LICCLIEN you must
  9.              build both DLLSERVE and LICSERVE first.  The main idea in
  10.              this code sample is to showcase licensing of components in
  11.              servers. To this end the LICSERVE server provides the
  12.              LicCruiseCar component as a licensed component and this
  13.              LICCLIEN module must access it through the IClassFactory2
  14.              licensing facilities.
  15.  
  16.              LICCLIEN composes its own native composite COM object,
  17.              COUtilityCruiseCar, by aggregating an existing licensed
  18.              COLicCruiseCar COM component (provided by the LICSERVE.DLL
  19.              server) with a native implementation of the IUtility
  20.              interface.  Thus, this app showcases nested aggregation using
  21.              COM's client/server protocol and involving a licensed
  22.              component.
  23.  
  24.              LICCLIEN also instantiates the car-related components
  25.              provided by the two servers and exercises them (ie, Car,
  26.              UtilityCar, LicCruiseCar, and UtilityCruiseCar).  LICCLIEN
  27.              provides a set of menus for these Car related objects with
  28.              selections for the respective methods of the Interfaces
  29.              exposed by those COM Objects.  LICCLIEN also exploits
  30.              LICSERVE's LicCarSample component to setup operation of that
  31.              server as a code sample that supports the Client's trace
  32.              LOGging macros.  After this is set up the server logs to the
  33.              Client's logging facility.  Similar logging is set up for the
  34.              DLLSERVE server.
  35.  
  36.              For a comprehensive tutorial code tour of LICCLIEN's
  37.              contents and offerings see the tutorial LICCLIEN.HTM file.
  38.              For more specific technical details on the internal workings
  39.              see the comments dispersed throughout the LICCLIEN source code.
  40.              For more details on the LICSERVE.DLL that LICCLIEN works with
  41.              see the LICSERVE.HTM file in the main tutorial directory.
  42.              For more details on the DLLSERVE.DLL that LICCLIEN works with
  43.              see the DLLSERVE.HTM file in the main tutorial directory.
  44.  
  45.   Classes:   CMainWindow
  46.  
  47.   Functions: InitApplication, WinMain
  48.  
  49.   Origin:    10-5-95: atrent - Editor-inheritance from the DLLCLIEN source.
  50.  
  51. ----------------------------------------------------------------------------
  52.   This file is part of the Microsoft ActiveX Tutorial Code Samples.
  53.  
  54.   Copyright (C) Microsoft Corporation, 1997.  All rights reserved.
  55.  
  56.   This source code is intended only as a supplement to Microsoft
  57.   Development Tools and/or on-line documentation.  See these other
  58.   materials for detailed information regarding Microsoft code samples.
  59.  
  60.   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  61.   KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  62.   IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  63.   PARTICULAR PURPOSE.
  64. ==========================================================================+*/
  65.  
  66. /*--------------------------------------------------------------------------
  67.   We include WINDOWS.H for all Win32 applications.
  68.   We include OLE2.H because we will be calling the COM/OLE Libraries.
  69.   We include INITGUID.H only once (here) in the entire app because we
  70.     will be defining GUIDs and want them as constants in the data segment.
  71.   We include OLECTL.H because we will using IClassFactory2.
  72.   We include COMMDLG.H because we will be using the Open File and
  73.     potentially other Common dialogs.
  74.   We include APPUTIL.H because we will be building this application using
  75.     the convenient Virtual Window and Dialog classes and other
  76.     utility functions in the APPUTIL Library (ie, APPUTIL.LIB).
  77.   We include ICARS.H and CARGUIDS.H for the common car-related Interface
  78.     class, GUID, and CLSID specifications.
  79.   We include LICCLIEN.H because it has class and resource definitions
  80.     specific to this LICCLIEN application.
  81.   We include UTCRUCAR.H because it has COUtilityCar object class.
  82. ---------------------------------------------------------------------------*/
  83. #include <windows.h>
  84. #include <ole2.h>
  85. #include <initguid.h>
  86. #include <olectl.h>
  87. #include <commdlg.h>
  88. #include <apputil.h>
  89. #include <icars.h>
  90. #include <carguids.h>
  91. #include "licclien.h"
  92. #include "utcrucar.h"
  93.  
  94.  
  95. // Here is a pointer for use by the global Trace Message logging macros.
  96. CMsgLog* g_pMsgLog;
  97.  
  98.  
  99. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  100.   Method:   CMainWindow::CMainWindow
  101.  
  102.   Summary:  CMainWindow Constructor.
  103.  
  104.   Args:     .
  105.  
  106.   Modifies: .
  107.  
  108.   Returns:  .
  109. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  110. CMainWindow::CMainWindow()
  111. {
  112.   // Ensure these member variable strings are null strings.
  113.   m_szFileName[0] = 0;
  114.   m_szFileTitle[0] = 0;
  115.  
  116.   // Fill in the Open File Name Common Dialog's OPENFILENAME structure.
  117.   m_ofnFile.lStructSize = sizeof(OPENFILENAME);
  118.   m_ofnFile.hwndOwner = m_hWnd;
  119.   m_ofnFile.hInstance = m_hInst;
  120.   m_ofnFile.lpstrFilter = TEXT(OFN_DEFAULTFILES_STR);
  121.   m_ofnFile.lpstrCustomFilter = NULL;
  122.   m_ofnFile.nMaxCustFilter = 0;
  123.   m_ofnFile.nFilterIndex = 1;
  124.   m_ofnFile.lpstrFile = m_szFileName;
  125.   m_ofnFile.nMaxFile = MAX_PATH;
  126.   m_ofnFile.lpstrInitialDir = TEXT(".");
  127.   m_ofnFile.lpstrFileTitle = m_szFileTitle;
  128.   m_ofnFile.nMaxFileTitle = MAX_PATH;
  129.   m_ofnFile.lpstrTitle = TEXT(OFN_DEFAULTTITLE_STR);
  130.   m_ofnFile.lpstrDefExt = NULL;
  131.   m_ofnFile.Flags = OFN_HIDEREADONLY;
  132.  
  133.   // NULL the license key string pointer.
  134.   m_bstrLicKey = NULL;
  135.  
  136.   // NULL all the main interface pointers.
  137.   m_pCar = NULL;
  138.   m_pUtilityCar = NULL;
  139.   m_pLicCruiseCar = NULL;
  140.   m_pUtilityCruiseCar = NULL;
  141.   m_pLicCarSample = NULL;
  142.   m_pDllCarSample = NULL;
  143. }
  144.  
  145.  
  146. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  147.   Method:   CMainWindow::~CMainWindow
  148.  
  149.   Summary:  CMainWindow Destructor.  Destruction of the main window
  150.             indicates that the application should quit and thus the
  151.             PostQuitMessage API is called.
  152.  
  153.   Args:     .
  154.  
  155.   Modifies: .
  156.  
  157.   Returns:  .
  158. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  159. CMainWindow::~CMainWindow()
  160. {
  161.   // CMainWindow is derived from CVirWindow which traps the WM_DESTROY
  162.   // message and causes a delete of CMainWindow which in turn causes this
  163.   // destructor to run. The WM_DESTROY results when the window is destoyed
  164.   // after a close of the window. Prior to exiting the main message loop:
  165.  
  166.   // We free any dynamically allocated data.
  167.   SysFreeString(m_bstrLicKey);
  168.  
  169.   // We release any and all of the pointers to instantiated COM objects.
  170.   RELEASE_INTERFACE(m_pCar);
  171.   RELEASE_INTERFACE(m_pUtilityCar);
  172.   RELEASE_INTERFACE(m_pLicCruiseCar);
  173.   RELEASE_INTERFACE(m_pUtilityCruiseCar);
  174.   RELEASE_INTERFACE(m_pLicCarSample);
  175.   RELEASE_INTERFACE(m_pDllCarSample);
  176.  
  177.   // We delete the CMsgBox and CMsgLog objects that were made in
  178.   // Initinstance.
  179.   DELETE_POINTER(m_pMsgBox);
  180.   DELETE_POINTER(m_pMsgLog);
  181.  
  182.   // We then post a WM_QUIT message to cause an exit of the main thread's
  183.   // message loop and an exit of this instance of the application.
  184.   PostQuitMessage(0);
  185. }
  186.  
  187.  
  188. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  189.   Method:   CMainWindow::InitInstance
  190.  
  191.   Summary:  Instantiates an instance of the main application window.
  192.             This method must be called only once, immediately after
  193.             window class construction.  We take care to delete 'this'
  194.             CMainWindow if we must return the error condition FALSE.
  195.  
  196.   Args:     HINSTANCE hInstance,
  197.               Handle of the application instance.
  198.             int nCmdShow)
  199.               Command to pass to ShowWindow.
  200.  
  201.   Modifies: m_szHelpFile, m_pMsgBox, m_pMsgLog.
  202.  
  203.   Returns:  BOOL.
  204.               TRUE if succeeded.
  205.               FALSE if failed.
  206. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  207. BOOL CMainWindow::InitInstance(
  208.        HINSTANCE hInstance,
  209.        int nCmdShow)
  210. {
  211.   BOOL bOk = FALSE;
  212.   HWND hWnd;
  213.  
  214.   // Create the Message Box and Message Log objects.
  215.   m_pMsgBox = new CMsgBox;
  216.   m_pMsgLog = new CMsgLog;
  217.  
  218.   if (NULL != m_pMsgBox && NULL != m_pMsgLog)
  219.   {
  220.     // Note, the Create method sets the m_hWnd member so we don't
  221.     // need to set it explicitly here first.
  222.  
  223.     // Here is the create of this window.  Size the window reasonably.
  224.     // Create sets both m_hInst and m_hWnd.
  225.     hWnd = Create(
  226.              TEXT(MAIN_WINDOW_CLASS_NAME_STR),
  227.              TEXT(MAIN_WINDOW_TITLE_STR),
  228.              WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX
  229.                | WS_MAXIMIZEBOX | WS_THICKFRAME,
  230.              CW_USEDEFAULT,
  231.              CW_USEDEFAULT,
  232.              ::GetSystemMetrics(SM_CXSCREEN)*3/5,
  233.              ::GetSystemMetrics(SM_CYSCREEN)*3/5,
  234.              NULL,
  235.              NULL,
  236.              hInstance);
  237.     if (hWnd)
  238.     {
  239.       // Ensure the new window is shown on screen and its content is
  240.       // painted.
  241.       ::ShowWindow(m_hWnd, nCmdShow);
  242.       ::UpdateWindow(m_hWnd);
  243.  
  244.       // Build a path to where the help file should be (it should be in
  245.       // the same directory as the .EXE but with the .HLP extension.
  246.       MakeFamilyPath(hInstance, m_szHelpFile, TEXT(HELP_FILE_EXT));
  247.  
  248.       // Init the Message Box object.
  249.       if (m_pMsgBox->Init(m_hInst, m_hWnd))
  250.       {
  251.         // Create the Trace Message Log ListBox as a child window that
  252.         //   fits the client area of the Main Window (the TRUE 3rd argument
  253.         //   specifies such an inside child).
  254.         // If you want the Trace Message Log in a separate (but owned)
  255.         //   window, then pass a FALSE instead for the 3rd argument.
  256.         if (m_pMsgLog->Create(m_hInst, m_hWnd, TRUE))
  257.         {
  258.           HRESULT hr;
  259.  
  260.           // Assign the global MsgLog pointer.
  261.           g_pMsgLog = m_pMsgLog;
  262.           // Use macro to log an initial start messsage.
  263.           LOGID(IDS_START_MESSAGE_LOG);
  264.  
  265.           LOG("C: Start LICSERVE Server Trace Logging.");
  266.           // Now ask COM for the ISample interface to the server's
  267.           // LicCarSample component.  This effectively loads the
  268.           // LICSERVE DLL.
  269.           hr = CoCreateInstance(
  270.                  CLSID_LicCarSample,
  271.                  NULL,
  272.                  CLSCTX_INPROC_SERVER,
  273.                  IID_ISample,
  274.                  (PPVOID)&m_pLicCarSample);
  275.           if (SUCCEEDED(hr))
  276.           {
  277.             hr = m_pLicCarSample->Init(m_hWnd, g_pMsgLog);
  278.             if (SUCCEEDED(hr))
  279.             {
  280.               HMENU hMenu  = ::GetMenu(m_hWnd);
  281.  
  282.               ::CheckMenuItem(
  283.                   hMenu,
  284.                   IDM_LOG_LICSERVER,
  285.                   MF_BYCOMMAND | MF_CHECKED);
  286.  
  287.               LOG("C: Start DLLSERVE Server Trace Logging.");
  288.               // Also set up logging from the subordinate DLLSERVE server
  289.               // to the this client as well.
  290.               hr = CoCreateInstance(
  291.                      CLSID_DllCarSample,
  292.                      NULL,
  293.                      CLSCTX_INPROC_SERVER,
  294.                      IID_ISample,
  295.                      (PPVOID)&m_pDllCarSample);
  296.               if (SUCCEEDED(hr))
  297.               {
  298.                 hr = m_pDllCarSample->Init(m_hWnd, g_pMsgLog);
  299.                 bOk = SUCCEEDED(hr);
  300.                 if (bOk)
  301.                 {
  302.                   ::CheckMenuItem(
  303.                       hMenu,
  304.                       IDM_LOG_DLLSERVER,
  305.                       MF_BYCOMMAND | MF_CHECKED);
  306.                 }
  307.                 else
  308.                 {
  309.                    RELEASE_INTERFACE(m_pDllCarSample);
  310.                    // We ask COM to unload any unused COM Servers.
  311.                    CoFreeUnusedLibraries();
  312.                 }
  313.               }
  314.               else
  315.                 m_pMsgBox->ErrorID(IDS_NODLLSERVER);
  316.             }
  317.             else
  318.             {
  319.               RELEASE_INTERFACE(m_pLicCarSample);
  320.               // We ask COM to unload any unused COM Servers.
  321.               CoFreeUnusedLibraries();
  322.             }
  323.           }
  324.           else
  325.             m_pMsgBox->ErrorID(IDS_NOLICSERVER);
  326.         }
  327.       }
  328.     }
  329.   }
  330.  
  331.   if (!bOk)
  332.   {
  333.     DELETE_POINTER(m_pMsgBox);
  334.     DELETE_POINTER(m_pMsgLog);
  335.   }
  336.  
  337.   return (bOk);
  338. }
  339.  
  340.  
  341. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  342.   Method:   CMainWindow::GetInterface
  343.  
  344.   Summary:  Convenience method that wraps the QueryInterface call
  345.             and accepts a main COM object IUnknown pointer as an argument.
  346.  
  347.   Args:     IUnknown* pObj,
  348.               Pointer to the COM Object we are getting an interface on.
  349.             REFIID riid,
  350.               The GUID for the interface that we are seeking.
  351.             PPVOID ppv);
  352.               Address of the caller's pointer variable that will
  353.               receive the requested interface pointer.
  354.  
  355.   Modifies: .
  356.  
  357.   Returns:  BOOL.
  358.               TRUE if succeeded.
  359.               FALSE if failed.
  360. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  361. BOOL CMainWindow::GetInterface(
  362.        IUnknown* pObj,
  363.        REFIID riid,
  364.        PPVOID ppv)
  365. {
  366.   BOOL bResult = FALSE;
  367.   HRESULT hr;
  368.  
  369.   *ppv=NULL;
  370.  
  371.   if (NULL != pObj)
  372.   {
  373.     LOG("C: --Obtaining Interface Pointer.");
  374.     hr=pObj->QueryInterface(riid, ppv);
  375.  
  376.     if (FAILED(hr))
  377.     {
  378.       LOGERROR("C: ???? QueryInterface", hr);
  379.     }
  380.     else
  381.     {
  382.       LOGF1("C: Interface obtained. *ppv=0x%X", *ppv);
  383.       bResult = TRUE;
  384.     }
  385.   }
  386.   else
  387.   {
  388.     LOG("C: ???? Create an object first.");
  389.   }
  390.  
  391.   return (bResult);
  392. }
  393.  
  394.  
  395. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  396.   Method:   CMainWindow::DoMenu
  397.  
  398.   Summary:  Dispatch and handle the main menu commands.
  399.  
  400.   Args:     WPARAM wParam,
  401.               First message parameter (word sized).
  402.             LPARAM lParam)
  403.               Second message parameter (long sized).
  404.  
  405.   Modifies: m_ofnFile, ...
  406.  
  407.   Returns:  LRESULT
  408.               Standard Windows WindowProc return value.
  409. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  410. LRESULT CMainWindow::DoMenu(
  411.           WPARAM wParam,
  412.           LPARAM lParam)
  413. {
  414.   LRESULT lResult = FALSE;
  415.   HRESULT hr;
  416.   HMENU hMenu  = ::GetMenu(m_hWnd);
  417.   IClassFactory2* pICF2LicCruiseCar;
  418.   // Here are some interface pointers used to call methods on our COCar,
  419.   // COUtilityCar, COLicCruiseCar, and COUtilityCruiseCar COM objects.
  420.   ICar* pICar;
  421.   IUtility* pIUtility;
  422.   ICruise* pICruise;
  423.  
  424.   switch (LOWORD(wParam))
  425.   {
  426.     //----------------------------------------------------------------------
  427.     // Handle File Menu Commands.
  428.     //----------------------------------------------------------------------
  429.     case IDM_FILE_EXIT:
  430.       // The user commands us to exit this application so we tell the
  431.       // Main window to close itself.
  432.       ::PostMessage(m_hWnd, WM_CLOSE, 0, 0);
  433.       break;
  434.  
  435.     //----------------------------------------------------------------------
  436.     // Handle Car Menu Commands.
  437.     //----------------------------------------------------------------------
  438.     case IDM_CAR_CREATE:
  439.       LOG("C: === Car Menu: Create.");
  440.       if (NULL == m_pCar)
  441.       {
  442.         // Call COM service to create an instance.
  443.         hr = CoCreateInstance(
  444.                CLSID_DllCar,
  445.                NULL,
  446.                CLSCTX_INPROC_SERVER,
  447.                IID_IUnknown,
  448.                (PPVOID)&m_pCar);
  449.         if (SUCCEEDED(hr))
  450.         {
  451.           ::CheckMenuItem(
  452.               hMenu,
  453.               IDM_CAR_CREATE,
  454.               MF_BYCOMMAND | MF_CHECKED);
  455.         }
  456.         else
  457.         {
  458.           LOGERROR("C: ???? CoCreateInstance",hr);
  459.         }
  460.       }
  461.       else
  462.         LOG("C: ???? Car already exists.");
  463.       break;
  464.     case IDM_CAR_RELEASE:
  465.       LOG("C: === Car Menu: Release.");
  466.       if (NULL != m_pCar)
  467.       {
  468.         RELEASE_INTERFACE(m_pCar);
  469.         // We ask COM to unload any unused COM Servers.
  470.         CoFreeUnusedLibraries();
  471.         ::CheckMenuItem(
  472.             hMenu,
  473.             IDM_CAR_CREATE,
  474.             MF_BYCOMMAND | MF_UNCHECKED);
  475.       }
  476.       else
  477.         LOG("C: ???? No Car to Release.");
  478.       break;
  479.     case IDM_CAR_SHIFT:
  480.       LOG("C: === Car Menu: ICar::Shift");
  481.       if (GetInterface(m_pCar, IID_ICar, (PPVOID)&pICar))
  482.       {
  483.         LOG("C: --Calling pICar->Shift");
  484.         pICar->Shift(1);
  485.         LOG("C: --Releasing pICar");
  486.         pICar->Release();
  487.       }
  488.       break;
  489.     case IDM_CAR_CLUTCH:
  490.       LOG("C: === Car Menu: ICar::Clutch");
  491.       if (GetInterface(m_pCar, IID_ICar, (PPVOID)&pICar))
  492.       {
  493.         LOG("C: --Calling pICar->Clutch");
  494.         pICar->Clutch(100);
  495.         LOG("C: --Releasing pICar");
  496.         pICar->Release();
  497.       }
  498.       break;
  499.     case IDM_CAR_SPEED:
  500.       LOG("C: === Car Menu: ICar::Speed");
  501.       if (GetInterface(m_pCar, IID_ICar, (PPVOID)&pICar))
  502.       {
  503.         LOG("C: --Calling pICar->Speed");
  504.         pICar->Speed(20);
  505.         LOG("C: --Releasing pICar");
  506.         pICar->Release();
  507.       }
  508.       break;
  509.     case IDM_CAR_STEER:
  510.       LOG("C: === Car Menu: ICar::Steer");
  511.       if (GetInterface(m_pCar, IID_ICar, (PPVOID)&pICar))
  512.       {
  513.         LOG("C: --Calling pICar->Steer");
  514.         pICar->Steer(0);
  515.         LOG("C: --Releasing pICar");
  516.         pICar->Release();
  517.       }
  518.       break;
  519.  
  520.     //----------------------------------------------------------------------
  521.     // Handle UtilityCar Menu Commands.
  522.     //----------------------------------------------------------------------
  523.     case IDM_UCAR_CREATE:
  524.       LOG("C: === UtilityCar Menu: Create.");
  525.       if (NULL == m_pUtilityCar)
  526.       {
  527.         // Call COM service to create an instance.
  528.         hr = CoCreateInstance(
  529.                CLSID_DllUtilityCar,
  530.                NULL,
  531.                CLSCTX_INPROC_SERVER,
  532.                IID_IUnknown,
  533.                (PPVOID)&m_pUtilityCar);
  534.         if (SUCCEEDED(hr))
  535.         {
  536.           ::CheckMenuItem(
  537.               hMenu,
  538.               IDM_UCAR_CREATE,
  539.               MF_BYCOMMAND | MF_CHECKED);
  540.         }
  541.         else
  542.         {
  543.           LOGERROR("C: ???? CoCreateInstance",hr);
  544.         }
  545.       }
  546.       else
  547.         LOG("C: ???? UtilityCar already exists.");
  548.       break;
  549.     case IDM_UCAR_RELEASE:
  550.       LOG("C: === UtilityCar Menu: Release.");
  551.       if (NULL != m_pUtilityCar)
  552.       {
  553.         RELEASE_INTERFACE(m_pUtilityCar);
  554.         // We ask COM to unload any unused COM Servers.
  555.         CoFreeUnusedLibraries();
  556.         ::CheckMenuItem(
  557.             hMenu,
  558.             IDM_UCAR_CREATE,
  559.             MF_BYCOMMAND | MF_UNCHECKED);
  560.       }
  561.       else
  562.         LOG("C: ???? No UtilityCar to Release.");
  563.       break;
  564.     case IDM_UCAR_SHIFT:
  565.       LOG("C: === UtilityCar Menu: ICar::Shift");
  566.       if (GetInterface(m_pUtilityCar, IID_ICar, (PPVOID)&pICar))
  567.       {
  568.         LOG("C: --Calling pICar->Shift");
  569.         pICar->Shift(2);
  570.         LOG("C: --Releasing pICar");
  571.         pICar->Release();
  572.       }
  573.       break;
  574.     case IDM_UCAR_CLUTCH:
  575.       LOG("C: === UtilityCar Menu: ICar::Clutch");
  576.       if (GetInterface(m_pUtilityCar, IID_ICar, (PPVOID)&pICar))
  577.       {
  578.         LOG("C: --Calling pICar->Clutch");
  579.         pICar->Clutch(100);
  580.         LOG("C: --Releasing pICar");
  581.         pICar->Release();
  582.       }
  583.       break;
  584.     case IDM_UCAR_SPEED:
  585.       LOG("C: === UtilityCar Menu: ICar::Speed");
  586.       if (GetInterface(m_pUtilityCar, IID_ICar, (PPVOID)&pICar))
  587.       {
  588.         LOG("C: --Calling pICar->Speed");
  589.         pICar->Speed(30);
  590.         LOG("C: --Releasing pICar");
  591.         pICar->Release();
  592.       }
  593.       break;
  594.     case IDM_UCAR_STEER:
  595.       LOG("C: === UtilityCar Menu: ICar::Steer");
  596.       if (GetInterface(m_pUtilityCar, IID_ICar, (PPVOID)&pICar))
  597.       {
  598.         LOG("C: --Calling pICar->Steer");
  599.         pICar->Steer(10);
  600.         LOG("C: --Releasing pICar");
  601.         pICar->Release();
  602.       }
  603.       break;
  604.     case IDM_UCAR_OFFROAD:
  605.       LOG("C: === UtilityCar Menu: IUtility::Offroad");
  606.       if (GetInterface(m_pUtilityCar, IID_IUtility, (PPVOID)&pIUtility))
  607.       {
  608.         LOG("C: --Calling pIUtility->Offroad");
  609.         pIUtility->Offroad(1);
  610.         LOG("C: --Releasing pIUtility");
  611.         pIUtility->Release();
  612.       }
  613.       break;
  614.     case IDM_UCAR_WINCH:
  615.       LOG("C: === UtilityCar Menu: IUtility::Winch");
  616.       if (GetInterface(m_pUtilityCar, IID_IUtility, (PPVOID)&pIUtility))
  617.       {
  618.         LOG("C: --Calling pIUtility->Winch");
  619.         pIUtility->Winch(0);
  620.         LOG("C: --Releasing pIUtility");
  621.         pIUtility->Release();
  622.       }
  623.       break;
  624.  
  625.     //----------------------------------------------------------------------
  626.     // Handle LicCruiseCar Menu Commands.
  627.     //----------------------------------------------------------------------
  628.     case IDM_CCAR_CREATE:
  629.       LOG("C: === LicCruiseCar Menu: Create.");
  630.       if (NULL == m_pLicCruiseCar)
  631.       {
  632.         // Call COM service to create an instance.
  633.         hr = CoCreateInstance(
  634.                CLSID_LicCruiseCar,
  635.                NULL,
  636.                CLSCTX_INPROC_SERVER,
  637.                IID_IUnknown,
  638.                (PPVOID)&m_pLicCruiseCar);
  639.         if (SUCCEEDED(hr))
  640.         {
  641.           ::CheckMenuItem(
  642.               hMenu,
  643.               IDM_CCAR_CREATE,
  644.               MF_BYCOMMAND | MF_CHECKED);
  645.         }
  646.         else
  647.         {
  648.           LOGERROR("C: ???? CoCreateInstance",hr);
  649.           if (CLASS_E_NOTLICENSED == hr)
  650.             m_pMsgBox->ErrorID(IDS_NOLICENSE);
  651.         }
  652.       }
  653.       else
  654.         LOG("C: ???? LicCruiseCar already exists.");
  655.       break;
  656.     case IDM_CCAR_RELEASE:
  657.       LOG("C: === LicCruiseCar Menu: Release.");
  658.       if (NULL != m_pLicCruiseCar)
  659.       {
  660.         RELEASE_INTERFACE(m_pLicCruiseCar);
  661.         // We ask COM to unload any unused COM Servers.
  662.         CoFreeUnusedLibraries();
  663.         ::CheckMenuItem(
  664.             hMenu,
  665.             IDM_CCAR_CREATE,
  666.             MF_BYCOMMAND | MF_UNCHECKED);
  667.       }
  668.       else
  669.         LOG("C: ???? No LicCruiseCar to Release.");
  670.       break;
  671.     case IDM_CCAR_SHIFT:
  672.       LOG("C: === LicCruiseCar Menu: ICar::Shift");
  673.       if (GetInterface(m_pLicCruiseCar, IID_ICar, (PPVOID)&pICar))
  674.       {
  675.         LOG("C: --Calling pICar->Shift");
  676.         pICar->Shift(4);
  677.         LOG("C: --Releasing pICar");
  678.         pICar->Release();
  679.       }
  680.       break;
  681.     case IDM_CCAR_CLUTCH:
  682.       LOG("C: === LicCruiseCar Menu: ICar::Clutch");
  683.       if (GetInterface(m_pLicCruiseCar, IID_ICar, (PPVOID)&pICar))
  684.       {
  685.         LOG("C: --Calling pICar->Clutch");
  686.         pICar->Clutch(100);
  687.         LOG("C: --Releasing pICar");
  688.         pICar->Release();
  689.       }
  690.       break;
  691.     case IDM_CCAR_SPEED:
  692.       LOG("C: === LicCruiseCar Menu: ICar::Speed");
  693.       if (GetInterface(m_pLicCruiseCar, IID_ICar, (PPVOID)&pICar))
  694.       {
  695.         LOG("C: --Calling pICar->Speed");
  696.         pICar->Speed(60);
  697.         LOG("C: --Releasing pICar");
  698.         pICar->Release();
  699.       }
  700.       break;
  701.     case IDM_CCAR_STEER:
  702.       LOG("C: === LicCruiseCar Menu: ICar::Steer");
  703.       if (GetInterface(m_pLicCruiseCar, IID_ICar, (PPVOID)&pICar))
  704.       {
  705.         LOG("C: --Calling pICar->Steer");
  706.         pICar->Steer(0);
  707.         LOG("C: --Releasing pICar");
  708.         pICar->Release();
  709.       }
  710.       break;
  711.     case IDM_CCAR_ENGAGE:
  712.       LOG("C: === LicCruiseCar Menu: ICruise::Engage");
  713.       if (GetInterface(m_pLicCruiseCar, IID_ICruise, (PPVOID)&pICruise))
  714.       {
  715.         LOG("C: --Calling pICruise->Engage");
  716.         pICruise->Engage(TRUE);
  717.         LOG("C: --Releasing pICruise");
  718.         pICruise->Release();
  719.       }
  720.       break;
  721.     case IDM_CCAR_ADJUST:
  722.       LOG("C: === LicCruiseCar Menu: ICruise::Adjust");
  723.       if (GetInterface(m_pLicCruiseCar, IID_ICruise, (PPVOID)&pICruise))
  724.       {
  725.         LOG("C: --Calling pICruise->Adjust");
  726.         pICruise->Adjust(FALSE);
  727.         LOG("C: --Releasing pICruise");
  728.         pICruise->Release();
  729.       }
  730.       break;
  731.  
  732.     //----------------------------------------------------------------------
  733.     // Handle UtilityCruiseCar Menu Commands.
  734.     //----------------------------------------------------------------------
  735.     case IDM_UCRU_GETLICKEY:
  736.       LOG("C: === UtilityCruiseCar Menu: Get License Key.");
  737.       // Get a class factory for LicCruiseCar and issue IClassFactory's
  738.       // CreateInstance method to manufacture a COLicCruiseCar COM object.
  739.       hr = CoGetClassObject(
  740.              CLSID_LicCruiseCar,
  741.              CLSCTX_INPROC_SERVER,
  742.              NULL,
  743.              IID_IClassFactory2,
  744.              (PPVOID)&pICF2LicCruiseCar);
  745.       if (SUCCEEDED(hr))
  746.       {
  747.         LOG("C: LicCruiseCar's IClassFactory2 obtained.");
  748.         hr = pICF2LicCruiseCar->RequestLicKey(0, &m_bstrLicKey);
  749.         pICF2LicCruiseCar->Release();
  750.       }
  751.  
  752.       if (SUCCEEDED(hr))
  753.       {
  754.         LOG("C: Obtained LicCruiseCar's Runtime License Key.");
  755.         ::CheckMenuItem(
  756.             hMenu,
  757.             IDM_UCRU_GETLICKEY,
  758.             MF_BYCOMMAND | MF_CHECKED);
  759.       }
  760.       else
  761.       {
  762.         LOGERROR("C: ???? CoGetClassObject",hr);
  763.         LOG("C: Failed to Obtain LicCruiseCar's Runtime License Key.");
  764.       }
  765.       break;
  766.     case IDM_UCRU_CLEARLICKEY:
  767.       LOG("C: === UtilityCruiseCar Menu: Clear License Key.");
  768.       SysFreeString(m_bstrLicKey);
  769.       m_bstrLicKey = NULL;
  770.       ::CheckMenuItem(
  771.           hMenu,
  772.           IDM_UCRU_GETLICKEY,
  773.           MF_BYCOMMAND | MF_UNCHECKED);
  774.       break;
  775.     case IDM_UCRU_CREATE:
  776.       LOG("C: === UtilityCruiseCar Menu: Create with Key.");
  777.       if (NULL == m_pUtilityCruiseCar)
  778.       {
  779.         // Call a create function to create an instance.
  780.         hr = CreateUtilityCruiseCar(
  781.                NULL,
  782.                IID_IUnknown,
  783.                m_bstrLicKey,
  784.                (PPVOID)&m_pUtilityCruiseCar);
  785.         if (SUCCEEDED(hr))
  786.         {
  787.           ::CheckMenuItem(
  788.               hMenu,
  789.               IDM_UCRU_CREATE,
  790.               MF_BYCOMMAND | MF_CHECKED);
  791.         }
  792.         else
  793.         {
  794.           LOGERROR("C: ???? UtilityCruiseCar creation",hr);
  795.           if (CLASS_E_NOTLICENSED == hr)
  796.             m_pMsgBox->ErrorID(IDS_NORUNLICENSE);
  797.         }
  798.       }
  799.       else
  800.         LOG("C: ???? UtilityCruiseCar already exists.");
  801.       break;
  802.     case IDM_UCRU_RELEASE:
  803.       LOG("C: === UtilityCruiseCar Menu: Release.");
  804.       if (NULL != m_pUtilityCruiseCar)
  805.       {
  806.         RELEASE_INTERFACE(m_pUtilityCruiseCar);
  807.         // We ask COM to unload any unused COM Servers.
  808.         CoFreeUnusedLibraries();
  809.         ::CheckMenuItem(
  810.             hMenu,
  811.             IDM_UCRU_CREATE,
  812.             MF_BYCOMMAND | MF_UNCHECKED);
  813.       }
  814.       else
  815.         LOG("C: ???? No UtilityCruiseCar to Release.");
  816.       break;
  817.     case IDM_UCRU_SHIFT:
  818.       LOG("C: === UtilityCruiseCar Menu: ICar::Shift");
  819.       if (GetInterface(m_pUtilityCruiseCar, IID_ICar, (PPVOID)&pICar))
  820.       {
  821.         LOG("C: --Calling pICar->Shift");
  822.         pICar->Shift(1);
  823.         LOG("C: --Releasing pICar");
  824.         pICar->Release();
  825.       }
  826.       break;
  827.     case IDM_UCRU_CLUTCH:
  828.       LOG("C: === UtilityCruiseCar Menu: ICar::Clutch");
  829.       if (GetInterface(m_pUtilityCruiseCar, IID_ICar, (PPVOID)&pICar))
  830.       {
  831.         LOG("C: --Calling pICar->Clutch");
  832.         pICar->Clutch(80);
  833.         LOG("C: --Releasing pICar");
  834.         pICar->Release();
  835.       }
  836.       break;
  837.     case IDM_UCRU_SPEED:
  838.       LOG("C: === UtilityCruiseCar Menu: ICar::Speed");
  839.       if (GetInterface(m_pUtilityCruiseCar, IID_ICar, (PPVOID)&pICar))
  840.       {
  841.         LOG("C: --Calling pICar->Speed");
  842.         pICar->Speed(10);
  843.         LOG("C: --Releasing pICar");
  844.         pICar->Release();
  845.       }
  846.       break;
  847.     case IDM_UCRU_STEER:
  848.       LOG("C: === UtilityCruiseCar Menu: ICar::Steer");
  849.       if (GetInterface(m_pUtilityCruiseCar, IID_ICar, (PPVOID)&pICar))
  850.       {
  851.         LOG("C: --Calling pICar->Steer");
  852.         pICar->Steer(10);
  853.         LOG("C: --Releasing pICar");
  854.         pICar->Release();
  855.       }
  856.       break;
  857.     case IDM_UCRU_ENGAGE:
  858.       LOG("C: === UtilityCruiseCar Menu: ICruise::Engage");
  859.       if (GetInterface(m_pUtilityCruiseCar, IID_ICruise, (PPVOID)&pICruise))
  860.       {
  861.         LOG("C: --Calling pICruise->Engage");
  862.         pICruise->Engage(FALSE);
  863.         LOG("C: --Releasing pICruise");
  864.         pICruise->Release();
  865.       }
  866.       break;
  867.     case IDM_UCRU_ADJUST:
  868.       LOG("C: === UtilityCruiseCar Menu: ICruise::Adjust");
  869.       if (GetInterface(m_pUtilityCruiseCar, IID_ICruise, (PPVOID)&pICruise))
  870.       {
  871.         LOG("C: --Calling pICruise->Adjust");
  872.         pICruise->Adjust(FALSE);
  873.         LOG("C: --Releasing pICruise");
  874.         pICruise->Release();
  875.       }
  876.       break;
  877.     case IDM_UCRU_OFFROAD:
  878.       LOG("C: === UtilityCruiseCar Menu: IUtility::Offroad");
  879.       if (GetInterface(m_pUtilityCruiseCar, IID_IUtility, (PPVOID)&pIUtility))
  880.       {
  881.         LOG("C: --Calling pIUtility->Offroad");
  882.         pIUtility->Offroad(3);
  883.         LOG("C: --Releasing pIUtility");
  884.         pIUtility->Release();
  885.       }
  886.       break;
  887.     case IDM_UCRU_WINCH:
  888.       LOG("C: === UtilityCruiseCar Menu: IUtility::Winch");
  889.       if (GetInterface(m_pUtilityCruiseCar, IID_IUtility, (PPVOID)&pIUtility))
  890.       {
  891.         LOG("C: --Calling pIUtility->Winch");
  892.         pIUtility->Winch(0);
  893.         LOG("C: --Releasing pIUtility");
  894.         pIUtility->Release();
  895.       }
  896.       break;
  897.  
  898.     //----------------------------------------------------------------------
  899.     // Handle Log Menu Commands.
  900.     //----------------------------------------------------------------------
  901.     case IDM_LOG_LOGCLEAR:
  902.       // Clear the message log.
  903.       m_pMsgLog->Clear();
  904.       // Use macro to log messages.
  905.       LOGID(IDS_START_MESSAGE_LOG);
  906.       break;
  907.     case IDM_LOG_LICSERVER:
  908.       // Toggle the state of the Message Logging from LICSERVE.
  909.       // Toggle the checkmark indicator on the menu selection as well.
  910.       {
  911.         BOOL bLogServer = ::GetMenuState(
  912.                             hMenu,
  913.                             IDM_LOG_LICSERVER,
  914.                             MF_BYCOMMAND) & MF_CHECKED;
  915.         if (bLogServer)
  916.         {
  917.           LOG("C: Stop LICSERVE Server Trace Logging.");
  918.           RELEASE_INTERFACE(m_pLicCarSample);
  919.           // We ask COM to unload any unused COM Servers.
  920.           CoFreeUnusedLibraries();
  921.           ::CheckMenuItem(
  922.               hMenu,
  923.               IDM_LOG_LICSERVER,
  924.               MF_BYCOMMAND | MF_UNCHECKED);
  925.         }
  926.         else
  927.         {
  928.           LOG("C: Start LICSERVE Server Trace Logging.");
  929.           hr = CoCreateInstance(
  930.                  CLSID_LicCarSample,
  931.                  NULL,
  932.                  CLSCTX_INPROC_SERVER,
  933.                  IID_ISample,
  934.                  (PPVOID)&m_pLicCarSample);
  935.           if (SUCCEEDED(hr))
  936.             hr = m_pLicCarSample->Init(m_hWnd, (PVOID)g_pMsgLog);
  937.  
  938.           if (SUCCEEDED(hr))
  939.           {
  940.             ::CheckMenuItem(
  941.                 hMenu,
  942.                 IDM_LOG_LICSERVER,
  943.                 MF_BYCOMMAND | MF_CHECKED);
  944.           }
  945.           else
  946.           {
  947.             RELEASE_INTERFACE(m_pLicCarSample);
  948.             // We ask COM to unload any unused COM Servers.
  949.             CoFreeUnusedLibraries();
  950.             m_pMsgBox->ErrorID(IDS_NOLICSERVER);
  951.           }
  952.         }
  953.       }
  954.       break;
  955.     case IDM_LOG_DLLSERVER:
  956.       // Toggle the state of the Message Logging from DLLSERVE.
  957.       // Toggle the checkmark indicator on the menu selection as well.
  958.       {
  959.         BOOL bLogServer = ::GetMenuState(
  960.                             hMenu,
  961.                             IDM_LOG_DLLSERVER,
  962.                             MF_BYCOMMAND) & MF_CHECKED;
  963.         if (bLogServer)
  964.         {
  965.           LOG("C: Stop DLLSERVE Server Trace Logging.");
  966.           RELEASE_INTERFACE(m_pDllCarSample);
  967.           // We ask COM to unload any unused COM Servers.
  968.           CoFreeUnusedLibraries();
  969.           ::CheckMenuItem(
  970.               hMenu,
  971.               IDM_LOG_DLLSERVER,
  972.               MF_BYCOMMAND | MF_UNCHECKED);
  973.         }
  974.         else
  975.         {
  976.           LOG("C: Start DLLSERVE Server Trace Logging.");
  977.           hr = CoCreateInstance(
  978.                  CLSID_DllCarSample,
  979.                  NULL,
  980.                  CLSCTX_INPROC_SERVER,
  981.                  IID_ISample,
  982.                  (PPVOID)&m_pDllCarSample);
  983.           if (SUCCEEDED(hr))
  984.             hr = m_pDllCarSample->Init(m_hWnd, (PVOID)g_pMsgLog);
  985.  
  986.           if (SUCCEEDED(hr))
  987.           {
  988.             ::CheckMenuItem(
  989.                 hMenu,
  990.                 IDM_LOG_DLLSERVER,
  991.                 MF_BYCOMMAND | MF_CHECKED);
  992.           }
  993.           else
  994.           {
  995.             RELEASE_INTERFACE(m_pDllCarSample);
  996.             // We ask COM to unload any unused COM Servers.
  997.             CoFreeUnusedLibraries();
  998.             m_pMsgBox->ErrorID(IDS_NODLLSERVER);
  999.           }
  1000.         }
  1001.       }
  1002.       break;
  1003.     case IDM_LOG_LOGGING:
  1004.       // Toggle the state of all Message Logging.
  1005.       // Toggle the checkmark indicator on the menu selection as well.
  1006.       {
  1007.         BOOL bLogging = ::GetMenuState(
  1008.                             hMenu,
  1009.                             IDM_LOG_LOGGING,
  1010.                             MF_BYCOMMAND) & MF_CHECKED;
  1011.         if (bLogging)
  1012.         {
  1013.           m_pMsgLog->Logging(FALSE);
  1014.           ::CheckMenuItem(
  1015.               hMenu,
  1016.               IDM_LOG_LOGGING,
  1017.               MF_BYCOMMAND | MF_UNCHECKED);
  1018.         }
  1019.         else
  1020.         {
  1021.           m_pMsgLog->Logging(TRUE);
  1022.           ::CheckMenuItem(
  1023.               hMenu,
  1024.               IDM_LOG_LOGGING,
  1025.               MF_BYCOMMAND | MF_CHECKED);
  1026.         }
  1027.       }
  1028.       break;
  1029.     case IDM_LOG_COPYCLIP:
  1030.       // Copy trace message log to clipboard.
  1031.       m_pMsgLog->Copy();
  1032.       break;
  1033.  
  1034.     //----------------------------------------------------------------------
  1035.     // Handle Help Menu Commands.
  1036.     //----------------------------------------------------------------------
  1037.     case IDM_HELP_CONTENTS:
  1038.       // We have some stubbed support here for bringing up the online
  1039.       //   Help for this application.
  1040.       if (::FileExist(m_szHelpFile))
  1041.         ::WinHelp(m_hWnd, m_szHelpFile, HELP_CONTEXT, IDH_CONTENTS);
  1042.       else
  1043.         m_pMsgBox->ErrorID(IDS_NOHELPFILE);
  1044.       break;
  1045.     case IDM_HELP_TUTORIAL:
  1046.       // Call the APPUTIL utility function, ReadTutorial, to Browse the HTML
  1047.       // tutorial narrative file associated with this tutorial code sample.
  1048.       ReadTutorial(m_hInst, m_hWnd, TEXT(HTML_FILE_EXT));
  1049.       break;
  1050.     case IDM_HELP_TUTLICSERVER:
  1051.       // Call the APPUTIL utility function, ReadTutorial, to Browse the HTML
  1052.       // tutorial narrative file associated with the LICSERVE server.
  1053.       ReadTutorial(m_hInst, m_hWnd, TEXT(LICSERVE_TUTFILE_STR));
  1054.       break;
  1055.     case IDM_HELP_TUTDLLSERVER:
  1056.       // Call the APPUTIL utility function, ReadTutorial, to Browse the HTML
  1057.       // tutorial narrative file associated with the DLLSERVE server.
  1058.       ReadTutorial(m_hInst, m_hWnd, TEXT(DLLSERVE_TUTFILE_STR));
  1059.       break;
  1060.     case IDM_HELP_READSOURCE:
  1061.       // Call the APPUTIL utility function ReadSource to allow the
  1062.       // user to open and read any of the source files of LICCLIEN.
  1063.       ReadSource(m_hWnd, &m_ofnFile);
  1064.       break;
  1065.     case IDM_HELP_ABOUT:
  1066.       {
  1067.         CAboutBox dlgAboutBox;
  1068.  
  1069.         LOG("C: === Help Menu: About LICCLIEN.");
  1070.         // Show the standard About Box dialog for this EXE by telling the
  1071.         // dialog C++ object to show itself by invoking its ShowDialog
  1072.         // method.  Pass it this EXE instance and the parent window handle.
  1073.         // Use a dialog resource ID for the dialog template stored in
  1074.         // this EXE module's resources.
  1075.         dlgAboutBox.ShowDialog(
  1076.           m_hInst,
  1077.           MAKEINTRESOURCE(IDM_HELP_ABOUT),
  1078.           m_hWnd);
  1079.       }
  1080.       break;
  1081.     case IDM_HELP_ABOUTLICSERVER:
  1082.       // Call the LicCarSample in LICSERVE to show its AboutBox dialog.
  1083.       LOG("C: === Help Menu: About LICSERVE.");
  1084.       if (NULL != m_pLicCarSample)
  1085.         m_pLicCarSample->AboutBox(m_hWnd);
  1086.       break;
  1087.     case IDM_HELP_ABOUTDLLSERVER:
  1088.       // Call the DllCarSample in DLLSERVE to show its AboutBox dialog.
  1089.       LOG("C: === Help Menu: About DLLSERVE.");
  1090.       if (NULL != m_pDllCarSample)
  1091.         m_pDllCarSample->AboutBox(m_hWnd);
  1092.       break;
  1093.  
  1094.     default:
  1095.       // Defer all messages NOT handled here to the Default Window Proc.
  1096.       lResult = ::DefWindowProc(m_hWnd, WM_COMMAND, wParam, lParam);
  1097.       break;
  1098.   }
  1099.  
  1100.   return(lResult);
  1101. }
  1102.  
  1103.  
  1104. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1105.   Method:   CMainWindow::WindowProc
  1106.  
  1107.   Summary:  Main window procedure for this window object.  See CVirWindow
  1108.             in the APPUTIL library (APPUTIL.CPP) for details on how this
  1109.             method gets called by the global WindowProc.
  1110.  
  1111.   Args:     UINT uMsg,
  1112.               Windows message that is "sent" to this window.
  1113.             WPARAM wParam,
  1114.               First message parameter (word sized).
  1115.             LPARAM lParam)
  1116.               Second message parameter (long sized).
  1117.  
  1118.   Modifies: ...
  1119.  
  1120.   Returns:  LRESULT
  1121.               Standard Windows WindowProc return value.
  1122. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1123. LRESULT CMainWindow::WindowProc(
  1124.           UINT uMsg,
  1125.           WPARAM wParam,
  1126.           LPARAM lParam)
  1127. {
  1128.   LRESULT lResult = FALSE;
  1129.  
  1130.   switch (uMsg)
  1131.   {
  1132.     case WM_CREATE:
  1133.       {
  1134.         // Setup for painting text in this window.
  1135.         HDC hdc = GetDC(m_hWnd);
  1136.         ::GetTextMetrics(hdc, &m_tm);
  1137.         ::ReleaseDC(m_hWnd, hdc);
  1138.       }
  1139.       break;
  1140.  
  1141.     case WM_MEASUREITEM:
  1142.       // Get setup for painting text in this window.
  1143.       {
  1144.         LPMEASUREITEMSTRUCT lpmis = (LPMEASUREITEMSTRUCT) lParam;
  1145.         lpmis->itemHeight = m_tm.tmHeight + m_tm.tmExternalLeading;
  1146.         lpmis->itemWidth = m_wWidth;
  1147.         lResult = TRUE;
  1148.       }
  1149.  
  1150.     case WM_SIZE:
  1151.       // Handle a resize of this window.
  1152.       m_wWidth = LOWORD(lParam);
  1153.       m_wHeight = HIWORD(lParam);
  1154.       // Resize the Message Log ListBox
  1155.       m_pMsgLog->Resize(m_wWidth, m_wHeight);
  1156.       break;
  1157.  
  1158.     case WM_COMMAND:
  1159.       // Dispatch and handle any Menu command messages received.
  1160.       lResult = DoMenu(wParam, lParam);
  1161.       break;
  1162.  
  1163.     case WM_CLOSE:
  1164.       // The user selected Close on the main window's System menu
  1165.       // or Exit on the File menu.
  1166.     case WM_QUIT:
  1167.       // If the app is being quit then close any associated help windows.
  1168.       // ::WinHelp(m_hWnd, m_szHelpFile, HELP_QUIT, 0);
  1169.     default:
  1170.       // Defer all messages NOT handled here to the Default Window Proc.
  1171.       lResult = ::DefWindowProc(m_hWnd, uMsg, wParam, lParam);
  1172.       break;
  1173.   }
  1174.  
  1175.   return(lResult);
  1176. }
  1177.  
  1178.  
  1179. /*F+F++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1180.   Function: UnicodeOk
  1181.  
  1182.   Summary:  Checks if the platform will handle unicode versions of
  1183.             Win32 string API calls.
  1184.  
  1185.   Args:     void
  1186.  
  1187.   Returns:  BOOL
  1188.               TRUE if unicode support; FALSE if not.
  1189. ------------------------------------------------------------------------F-F*/
  1190. BOOL UnicodeOk(void)
  1191. {
  1192.   BOOL bOk = TRUE;
  1193.   TCHAR szUserName[MAX_STRING_LENGTH];
  1194.   DWORD dwSize = MAX_STRING_LENGTH;
  1195.  
  1196.   if (!GetUserName(szUserName, &dwSize))
  1197.     bOk = ERROR_CALL_NOT_IMPLEMENTED == GetLastError() ? FALSE : TRUE;
  1198.  
  1199.   return bOk;
  1200. }
  1201.  
  1202.  
  1203. /*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
  1204.   Function: InitApplication
  1205.  
  1206.   Summary:  Initializes the application and registers its main window
  1207.             class. InitApplication is called only once (in WinMain).
  1208.  
  1209.   Args:     HINSTANCE hInstance)
  1210.               Handle to the first instance of the application.
  1211.  
  1212.   Returns:  BOOL.
  1213.               TRUE if success.
  1214.               FALSE if fail.
  1215. F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
  1216. BOOL InitApplication(
  1217.        HINSTANCE hInstance)
  1218. {
  1219.   BOOL bOK;
  1220.   // The window class for all instances of the main frame window.
  1221.   WNDCLASSEX wcf;
  1222.  
  1223.   // Assign the appropriate values for this main frame window class.
  1224.   wcf.cbSize        = sizeof(WNDCLASSEX);
  1225.   wcf.style         = CS_HREDRAW | CS_VREDRAW; // Class style(s).
  1226.   wcf.lpfnWndProc   = &WindowProc;             // Global Window Procedure for
  1227.                                                //   all windows of this class.
  1228.   wcf.cbClsExtra    = 0;                       // No per-class extra data.
  1229.   wcf.cbWndExtra    = 0;                       // No per-window extra data.
  1230.   wcf.hInstance     = hInstance;               // Owner of this class.
  1231.   wcf.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);       // Default color.
  1232.   wcf.lpszMenuName  = TEXT(MAIN_WINDOW_CLASS_MENU_STR); // Menu name from .RC.
  1233.   wcf.lpszClassName = TEXT(MAIN_WINDOW_CLASS_NAME_STR); // Class name from .RC.
  1234.   wcf.hCursor       = LoadCursor(NULL, IDC_ARROW);      // Cursor.
  1235.   wcf.hIcon         = LoadIcon(                         // Icon name from .RC.
  1236.                         hInstance,
  1237.                         TEXT("AppIcon"));
  1238.   wcf.hIconSm       = LoadImage(                        // Load small icon.
  1239.                         hInstance,
  1240.                         TEXT("AppIcon"),
  1241.                         IMAGE_ICON,
  1242.                         16, 16,
  1243.                         0);
  1244.  
  1245.   // Register the window class and return FALSE if unsuccesful.
  1246.   bOK = RegisterClassEx(&wcf);
  1247.   if (!bOK)
  1248.   {
  1249.     // Assume we are running on NT where RegisterClassEx() is
  1250.     // not implemented, so let's try calling RegisterClass().
  1251.     bOK = RegisterClass((LPWNDCLASS)&wcf.style);
  1252.   }
  1253.  
  1254.   return (bOK);
  1255. }
  1256.  
  1257.  
  1258. /*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
  1259.   Function: WinMain
  1260.  
  1261.   Summary:  The Windows main entry point function for this application.
  1262.             Initializes the application, the COM Libraries, and starts
  1263.             the main application message loop.
  1264.  
  1265.   Args:     HINSTANCE hInstance,
  1266.               Instance handle; a new one for each invocation of this app.
  1267.             HINSTANCE hPrevInstance,
  1268.               Instance handle of the previous instance. NULL in Win32.
  1269.             LPSTR lpCmdLine,
  1270.               Windows passes a pointer to the application's
  1271.               invocation command line.
  1272.             int nCmdShow)
  1273.               Bits telling the show state of the application.
  1274.  
  1275.   Returns:  int
  1276.               msg.wParam (upon exit of message loop).
  1277.               FALSE if this instance couldn't initialize and run.
  1278. F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
  1279. extern "C" int PASCAL WinMain(
  1280.                         HINSTANCE hInstance,
  1281.                         HINSTANCE hPrevInstance,
  1282.                         LPSTR lpCmdLine,
  1283.                         int nCmdShow)
  1284. {
  1285.   CMainWindow* pWin = NULL;
  1286.   MSG msg;
  1287.   HACCEL hAccel;
  1288.   int iRun = FALSE;
  1289.  
  1290.   // If we were compiled for UNICODE and the platform seems OK with this
  1291.   // then proceed.  Else we error and exit the app.
  1292.   if (UnicodeOk())
  1293.   {
  1294.     // Call to initialize the COM Library.  Use the SUCCEEDED macro
  1295.     // to detect success.  If fail then exit app with error message.
  1296.     if (SUCCEEDED(CoInitialize(NULL)))
  1297.     {
  1298.       // If we succeeded in initializing the COM Library we proceed to
  1299.       // initialize the application.  If we can't init the application
  1300.       // then we signal shut down with an error message exit.
  1301.       iRun = InitApplication(hInstance);
  1302.       if (iRun)
  1303.       {
  1304.         // Assume we'll set iRun to TRUE when initialization is done.
  1305.         iRun = FALSE;
  1306.         // We are still go for running so we try to create a nifty new
  1307.         // CMainWindow object for this app instance.
  1308.         pWin = new CMainWindow;
  1309.         if (NULL != pWin)
  1310.         {
  1311.           // Now we initialize an instance of the new CMainWindow.
  1312.           // This includes creating the main window.  Note: if
  1313.           // InitInstance fails then it would have already deleted
  1314.           // pWin so we wouldn't need to delete it here.
  1315.           if (pWin->InitInstance(hInstance, nCmdShow))
  1316.           {
  1317.             // Load the keyboard accelerators from the resources.
  1318.             hAccel = LoadAccelerators(hInstance, TEXT("AppAccel"));
  1319.             if (NULL != hAccel)
  1320.             {
  1321.               // Signal App Initialization is successfully done.
  1322.               iRun = TRUE;
  1323.             }
  1324.           }
  1325.         }
  1326.       }
  1327.  
  1328.       if (iRun)
  1329.       {
  1330.         // If we initialized the app instance properly then we are still
  1331.         // go for running.  We then start up the main message pump for
  1332.         // the application.
  1333.         while (GetMessage(&msg, NULL, 0, 0))
  1334.         {
  1335.           if (!TranslateAccelerator(
  1336.                  pWin->GetHwnd(),
  1337.                  hAccel,
  1338.                  &msg))
  1339.           {
  1340.             TranslateMessage(&msg);
  1341.             DispatchMessage(&msg);
  1342.           }
  1343.         }
  1344.  
  1345.         // We ask COM to unload any unused COM Servers.
  1346.         CoFreeUnusedLibraries();
  1347.  
  1348.         // We'll pass to Windows the reason why we exited the message loop.
  1349.         iRun = msg.wParam;
  1350.       }
  1351.       else
  1352.       {
  1353.         // We failed to initialize the application--issue an error
  1354.         // messagebox.
  1355.         TCHAR szMsg[MAX_STRING_LENGTH];
  1356.  
  1357.         // Load the error message string from the resources.
  1358.         if (LoadString(
  1359.               hInstance,
  1360.               IDS_APPINITFAILED,
  1361.               szMsg,
  1362.               MAX_STRING_LENGTH))
  1363.         {
  1364.           // Put up error message box saying that application couldn't be
  1365.           // initialized.  Parent window is desktop (ie, NULL).
  1366.           MessageBox(
  1367.             NULL,
  1368.             szMsg,
  1369.             TEXT(ERROR_TITLE_STR),
  1370.             MB_OK | MB_ICONEXCLAMATION);
  1371.         }
  1372.         DELETE_POINTER(pWin);
  1373.       }
  1374.  
  1375.       // We're exiting this app (either normally or by init failure) so
  1376.       // shut down the COM Library.
  1377.       CoUninitialize();
  1378.     }
  1379.     else
  1380.     {
  1381.       // We failed to Initialize the COM Library.
  1382.       TCHAR szMsg[MAX_STRING_LENGTH];
  1383.  
  1384.       // Load the error message string from the resources.
  1385.       if (LoadString(
  1386.             hInstance,
  1387.             IDS_COMINITFAILED,
  1388.             szMsg,
  1389.             MAX_STRING_LENGTH))
  1390.       {
  1391.         // Put up error message box saying that COM Library
  1392.         // couldn't be initialized.  Parent window is desktop (ie, NULL).
  1393.         // And exit the failed application.
  1394.         MessageBox(
  1395.           NULL,
  1396.           szMsg,
  1397.           TEXT(ERROR_TITLE_STR),
  1398.           MB_OK | MB_ICONEXCLAMATION);
  1399.       }
  1400.     }
  1401.   }
  1402.   else
  1403.   {
  1404.     // If we were compiled for UNICODE but the platform has problems with
  1405.     // this then indicate an error and exit the app immediately.
  1406.     CHAR szMsg[MAX_STRING_LENGTH];
  1407.  
  1408.     if (LoadStringA(
  1409.           hInstance,
  1410.           IDS_NOUNICODE,
  1411.           szMsg,
  1412.           MAX_STRING_LENGTH))
  1413.     {
  1414.       MessageBoxA(
  1415.         NULL,
  1416.         szMsg,
  1417.         ERROR_TITLE_STR,
  1418.         MB_OK | MB_ICONEXCLAMATION);
  1419.     }
  1420.   }
  1421.  
  1422.   return iRun;
  1423. }
  1424.