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 / aptclien / aptclien.cpp next >
C/C++ Source or Header  |  1997-08-30  |  40KB  |  1,182 lines

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