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 / remclien / remclien.cpp < prev    next >
C/C++ Source or Header  |  1997-08-30  |  45KB  |  1,341 lines

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