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 / apputil / apputil.cpp next >
Text File  |  1997-09-12  |  78KB  |  2,641 lines

  1. /*+==========================================================================
  2.   File:      APPUTIL.CPP
  3.  
  4.   Summary:   Implementation file for the general application utility
  5.              classes and functions offered by the APPUTIL library.
  6.  
  7.              For a comprehensive tutorial code tour of APPUTIL's
  8.              contents and offerings see the tutorial APPUTIL.HTM file.
  9.              For more specific details see the comments dispersed
  10.              throughout the APPUTIL source code.
  11.  
  12.   Classes:   CVirWindow, CVirDialog, CAboutBox, CDelayBox, CMsgBox, CMsgLog,
  13.              CSendLog, CThreaded.
  14.  
  15.   Functions: WindowProc, DialogProc, SkipAnsi, FileExist, MakeFamilyPath,
  16.              GetExeName, CmdExec, HrMsg, HrMsgId, ReadHelp, ReadTutorial,
  17.              GoWeb, ReadSource, OutputDebugFmt, DComOk, lRandom, AnsiToUc,
  18.              UcToAnsi, A_StringFromGUID2, A_WriteFmtUserTypeStg,
  19.              A_StgIsStorageFile, A_StgCreateDocfile, A_StgOpenStorage,
  20.              DelayBox, ErrorBox, CreateColorScalePalette, PaintWindow,
  21.              GetErrorMsg.
  22.  
  23.   Origin:    8-28-97: atrent - Revised (based on WINHLPRS by stevebl).
  24.  
  25. ----------------------------------------------------------------------------
  26.   This file is part of the Microsoft COM Tutorial Code Samples.
  27.  
  28.   Copyright (C) Microsoft Corporation, 1997.  All rights reserved.
  29.  
  30.   This source code is intended only as a supplement to Microsoft
  31.   Development Tools and/or on-line documentation.  See these other
  32.   materials for detailed information regarding Microsoft code samples.
  33.  
  34.   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  35.   KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  36.   IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  37.   PARTICULAR PURPOSE.
  38. ==========================================================================+*/
  39.  
  40. /*---------------------------------------------------------------------------
  41.   We include WINDOWS.H for all Win32 applications.
  42.   We include TCHAR.H for general Unicode/Ansi prototype of utility
  43.     functions like _tsplitpath, etc.
  44.   We include SHELLAPI.H for the shell execute functions used to read
  45.     the tutorial HTM pages.
  46.   We include APPUTIL.H for the defines of this APPUTIL library.  We use
  47.     the _NOANSIMACROS_ to turn off the ANSI macro undefines so that
  48.     function calls to certain COM APIs inside this module get to the
  49.     original COM library functions instead of the ANSI surrogates.
  50. ---------------------------------------------------------------------------*/
  51. #include <windows.h>
  52. #include <tchar.h>
  53. #include <shellapi.h>
  54. #define _NOANSIMACROS_
  55. #include "apputil.h"
  56.  
  57.  
  58. /*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
  59.   Function: AnsiToUc
  60.  
  61.   Summary:  Convert an ANSI 'multibyte' string into a UNICODE 'wide
  62.             character' string.
  63.  
  64.   Args:     LPSTR pszAnsi
  65.               Pointer to a caller's input ANSI string.
  66.             LPWSTR pwszUc
  67.               Pointer to a caller's output UNICODE wide string.
  68.             int cch
  69.               Character count. If 0 then use length of pszAnsi.
  70.  
  71.   Returns:  HRESULT
  72.               Standard result code. NOERROR for success.
  73. F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
  74. HRESULT AnsiToUc(
  75.           LPSTR pszAnsi,
  76.           LPWSTR pwszUc,
  77.           int cch)
  78. {
  79.   HRESULT hr = E_POINTER;
  80.   int cSize;
  81.   int cOut;
  82.  
  83.   if (NULL != pszAnsi && NULL != pwszUc)
  84.   {
  85.     if (0 == cch)
  86.       cSize = MultiByteToWideChar(CP_ACP, 0, pszAnsi, -1, NULL, 0);
  87.     else
  88.       cSize = cch;
  89.  
  90.     if (0 != cSize)
  91.     {
  92.       cOut = MultiByteToWideChar(CP_ACP, 0, pszAnsi, -1, pwszUc, cSize);
  93.  
  94.       if (0 != cOut)
  95.         hr = NOERROR;
  96.     }
  97.     else
  98.       hr = E_FAIL;
  99.   }
  100.  
  101.   return hr;
  102. }
  103.  
  104.  
  105. /*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
  106.   Function: UcToAnsi
  107.  
  108.   Summary:  Convert a UNICODE 'wide character' input string to an output
  109.             ANSI 'multi-byte' string.
  110.  
  111.   Args:     LPWSTR pwszUc
  112.               Pointer to a caller's input UNICODE wide string.
  113.             LPSTR pszAnsi
  114.               Pointer to a caller's output ANSI string.
  115.             int cch
  116.               Character count. If 0 then use length of pszUc.
  117.  
  118.   Returns:  HRESULT
  119.               Standard result code. NOERROR for success.
  120. F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
  121. HRESULT UcToAnsi(
  122.           LPWSTR pwszUc,
  123.           LPSTR pszAnsi,
  124.           int cch)
  125. {
  126.   HRESULT hr = E_POINTER;
  127.   int cSize;
  128.   int cOut;
  129.  
  130.   if (NULL != pszAnsi && NULL != pwszUc)
  131.   {
  132.     if (0 == cch)
  133.       cSize = WideCharToMultiByte(CP_ACP,0,pwszUc,-1,NULL,0,NULL,NULL);
  134.     else
  135.       cSize = cch;
  136.  
  137.     if (0 != cSize)
  138.     {
  139.       cOut = WideCharToMultiByte(CP_ACP,0,pwszUc,-1,pszAnsi,cSize,NULL,NULL);
  140.       if (0 != cOut)
  141.         hr = NOERROR;
  142.     }
  143.     else
  144.       hr = E_FAIL;
  145.   }
  146.  
  147.   return hr;
  148. }
  149.  
  150.  
  151. #if !defined(UNICODE)
  152.  
  153.  
  154. /*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
  155.   Function: A_StringFromGUID2
  156.  
  157.   Summary:  ANSI Surrogate for the Unicode API call.
  158. F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
  159. STDAPI A_StringFromGUID2(REFGUID guid, LPSTR pszGUID, int cch)
  160. {
  161.   HRESULT hr = E_INVALIDARG;
  162.   LPWSTR  pszUc;
  163.   IMalloc* pIMalloc;
  164.  
  165.   if (NULL != pszGUID && 0 < cch)
  166.   {
  167.     hr = CoGetMalloc(MEMCTX_TASK, &pIMalloc);
  168.     if (SUCCEEDED(hr))
  169.     {
  170.       pszUc = (LPWSTR)pIMalloc->Alloc((cch+1)*sizeof(TCHAR));
  171.       pIMalloc->Release();
  172.       if (NULL != pszUc)
  173.       {
  174.         hr = StringFromGUID2(guid, pszUc, cch);
  175.         if (SUCCEEDED(hr))
  176.           hr = UcToAnsi(pszUc, pszGUID, cch);
  177.         CoTaskMemFree((void *)pszUc);
  178.       }
  179.       else
  180.         hr = E_OUTOFMEMORY;
  181.     }
  182.   }
  183.  
  184.   return hr;
  185. }
  186.  
  187.  
  188. /*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
  189.   Function: A_WriteFmtUserTypeStg
  190.  
  191.   Summary:  ANSI Surrogate for the Unicode API call.
  192. F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
  193. STDAPI A_WriteFmtUserTypeStg(
  194.          IStorage* pIStorage,
  195.          CLIPFORMAT ClipFmt,
  196.          LPSTR pszUserType)
  197. {
  198.   HRESULT hr = E_INVALIDARG;
  199.   WCHAR wszUc[MAX_PATH];
  200.  
  201.   if (NULL != pszUserType)
  202.   {
  203.     AnsiToUc(pszUserType, wszUc, MAX_PATH);
  204.     hr = WriteFmtUserTypeStg(pIStorage, ClipFmt, wszUc);
  205.   }
  206.  
  207.   return hr;
  208. }
  209.  
  210.  
  211. /*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
  212.   Function: A_StgIsStorageFile
  213.  
  214.   Summary:  ANSI Surrogate for the Unicode API call.
  215. F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
  216. STDAPI A_StgIsStorageFile(
  217.          LPSTR pszFileName)
  218. {
  219.   HRESULT hr = E_INVALIDARG;
  220.   WCHAR wszUc[MAX_PATH];
  221.  
  222.   if (NULL != pszFileName)
  223.   {
  224.     AnsiToUc(pszFileName, wszUc, MAX_PATH);
  225.     hr = StgIsStorageFile(wszUc);
  226.   }
  227.  
  228.   return hr;
  229. }
  230.  
  231.  
  232. /*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
  233.   Function: A_StgCreateDocfile
  234.  
  235.   Summary:  ANSI Surrogate for the Unicode API call.
  236. F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
  237. STDAPI A_StgCreateDocfile(
  238.          LPSTR pszFileName,
  239.          DWORD dwAccessMode,
  240.          DWORD reserved,
  241.          IStorage** ppIStorage)
  242. {
  243.   HRESULT hr = E_INVALIDARG;
  244.   WCHAR wszUc[MAX_PATH];
  245.   LPWSTR pwszUc = NULL;
  246.  
  247.   // Null the caller's output variable.
  248.   *ppIStorage = NULL;
  249.  
  250.   if (NULL != pszFileName)
  251.   {
  252.     AnsiToUc(pszFileName, wszUc, MAX_PATH);
  253.     pwszUc = wszUc;
  254.   }
  255.  
  256.   hr = StgCreateDocfile(pwszUc, dwAccessMode, reserved, ppIStorage);
  257.  
  258.   return hr;
  259. }
  260.  
  261.  
  262. /*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
  263.   Function: A_StgOpenStorage
  264.  
  265.   Summary:  ANSI Surrogate for the Unicode API call.
  266. F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
  267. STDAPI A_StgOpenStorage(
  268.          LPSTR pszFileName,
  269.          IStorage* pIStorage,
  270.          DWORD dwAccessMode,
  271.          SNB snbExclude,
  272.          DWORD reserved,
  273.          IStorage** ppIStorage)
  274. {
  275.   HRESULT hr = E_INVALIDARG;
  276.   WCHAR wszUc[MAX_PATH];
  277.   LPWSTR pwszUc = NULL;
  278.  
  279.   // Null the caller's output variable.
  280.   *ppIStorage = NULL;
  281.  
  282.   if (NULL != pszFileName)
  283.   {
  284.     AnsiToUc(pszFileName, wszUc, MAX_PATH);
  285.     pwszUc = wszUc;
  286.   }
  287.  
  288.   hr = StgOpenStorage(
  289.          pwszUc,
  290.          pIStorage,
  291.          dwAccessMode,
  292.          snbExclude,
  293.          reserved,
  294.          ppIStorage);
  295.  
  296.   return hr;
  297. }
  298.  
  299.  
  300. #endif  // !UNICODE
  301.  
  302.  
  303. /*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
  304.   Function: DComOk
  305.  
  306.   Summary:  Determine if DCOM (Distributed COM) can be used. It can if it
  307.             is installed on the current machine. This will always succeed
  308.             under WinNT 4.x or above. I will succeed on Win95 only if the
  309.             DCOM95 addon is installed.
  310.  
  311.   Args:     void
  312.  
  313.   Returns:  BOOL
  314.               TRUE - DCOM installed; FALSE - NOT.
  315. F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
  316. BOOL DComOk(void)
  317. {
  318.   BOOL bOk = FALSE;
  319.  
  320.   if (NULL != GetProcAddress(
  321.                 GetModuleHandle(TEXT("OLE32")),
  322.                 "CoInitializeEx"))
  323.     bOk = TRUE;
  324.  
  325.   return(bOk);
  326. }
  327.  
  328.  
  329. /*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
  330.   Function: lRandom
  331.  
  332.   Summary:  Simple random number generator. Returns a random DWORD.
  333.  
  334.   Args:     void
  335.  
  336.   Returns:  DWORD
  337.               Random number.
  338. F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
  339. DWORD lRandom(void)
  340. {
  341.   static DWORD glSeed = (DWORD)-365387184;
  342.  
  343.   glSeed *= 69069;
  344.  
  345.   return(++glSeed);
  346. }
  347.  
  348.  
  349. /*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
  350.   Function: CreateColorScalePalette
  351.  
  352.   Summary:  This function creates a palette representing the scale values
  353.             of a particular RGB color.  A gray-scale palette can also be
  354.             created. Borrowed from GDIDEMO in the Win32 samples of the
  355.             Win32 SDK.
  356.  
  357.   Args:     HDC hDC,
  358.               Handle to device context.
  359.             int nColor
  360.               New color.
  361.  
  362.   Returns:  HPALETTE
  363.               Handle to new pallete.
  364. F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
  365. HPALETTE CreateColorScalePalette(
  366.            HDC hDC,
  367.            int nColor)
  368. {
  369.   HPALETTE     hPalette;
  370.   GLOBALHANDLE hMem;
  371.   LPLOGPALETTE lpMem;
  372.   int          idx,nReserved,nSize;
  373.  
  374.   hPalette = NULL;
  375.   if(GetDeviceCaps(hDC,RASTERCAPS) & RC_PALETTE)
  376.   {
  377.     nReserved = GetDeviceCaps(hDC,NUMRESERVED);
  378.     nSize     = GetDeviceCaps(hDC,SIZEPALETTE) - nReserved;
  379.  
  380.     hMem = GlobalAlloc(
  381.              GHND,
  382.              (DWORD)sizeof(LOGPALETTE)+(sizeof(PALETTEENTRY)*nSize));
  383.     if(hMem)
  384.     {
  385.       if(lpMem = (LPLOGPALETTE)GlobalLock(hMem))
  386.       {
  387.         lpMem->palNumEntries = (WORD)nSize;
  388.         lpMem->palVersion    = (WORD)0x0300;
  389.         switch(nColor)
  390.         {
  391.           case COLOR_SCALE_RED:
  392.             for(idx=0; idx < nSize; idx++)
  393.             {
  394.               lpMem->palPalEntry[idx].peRed   = (BYTE)idx;
  395.               lpMem->palPalEntry[idx].peGreen = 0;
  396.               lpMem->palPalEntry[idx].peBlue  = 0;
  397.               lpMem->palPalEntry[idx].peFlags = PC_RESERVED;
  398.             }
  399.             break;
  400.  
  401.           case COLOR_SCALE_GREEN:
  402.             for(idx=0; idx < nSize; idx++)
  403.             {
  404.               lpMem->palPalEntry[idx].peRed   = 0;
  405.               lpMem->palPalEntry[idx].peGreen = (BYTE)idx;
  406.               lpMem->palPalEntry[idx].peBlue  = 0;
  407.               lpMem->palPalEntry[idx].peFlags = PC_RESERVED;
  408.             }
  409.             break;
  410.  
  411.           case COLOR_SCALE_BLUE:
  412.             for(idx=0; idx < nSize; idx++)
  413.             {
  414.               lpMem->palPalEntry[idx].peRed   = 0;
  415.               lpMem->palPalEntry[idx].peGreen = 0;
  416.               lpMem->palPalEntry[idx].peBlue  = (BYTE)idx;
  417.               lpMem->palPalEntry[idx].peFlags = PC_RESERVED;
  418.             }
  419.             break;
  420.  
  421.           default:
  422.           case COLOR_SCALE_GRAY:
  423.             for(idx=0; idx < nSize; idx++)
  424.             {
  425.               lpMem->palPalEntry[idx].peRed   = (BYTE)idx;
  426.               lpMem->palPalEntry[idx].peGreen = (BYTE)idx;
  427.               lpMem->palPalEntry[idx].peBlue  = (BYTE)idx;
  428.               lpMem->palPalEntry[idx].peFlags = PC_RESERVED;
  429.             }
  430.             break;
  431.         }
  432.  
  433.         hPalette = CreatePalette(lpMem);
  434.  
  435.         GlobalUnlock(hMem);
  436.       }
  437.       GlobalFree(hMem);
  438.     }
  439.   }
  440.  
  441.   return(hPalette);
  442. }
  443.  
  444.  
  445. /*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
  446.   Function: PaintWindow
  447.  
  448.   Summary:  This function is used to paint the background of a window.
  449.             Borrowed from GDIDEMO in the Win32 samples of the Win32 SDK.
  450.  
  451.   Args:     HWND hWnd,
  452.               Window handle.
  453.             int nColor
  454.               New window color.
  455.  
  456.   Returns:  void.
  457. F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
  458. VOID PaintWindow(HWND hWnd, int nColor)
  459. {
  460.   HDC         hDC;
  461.   int         nMapMode,idx,nSize,nReserved,nLoop;
  462.   RECT        rect;
  463.   HBRUSH      hBrush;
  464.   PAINTSTRUCT ps;
  465.   HPALETTE    hPal;
  466.  
  467.   if(hDC = BeginPaint(hWnd,&ps))
  468.   {
  469.     GetClientRect(hWnd,&rect);
  470.     nMapMode = SetMapMode(hDC,MM_ANISOTROPIC);
  471.  
  472.     if(GetDeviceCaps(hDC,RASTERCAPS) & RC_PALETTE)
  473.     {
  474.       nReserved = GetDeviceCaps(hDC,NUMRESERVED);
  475.       nSize     = GetDeviceCaps(hDC,SIZEPALETTE) - nReserved;
  476.  
  477.       if(hPal = CreateColorScalePalette(hDC,nColor))
  478.       {
  479.         hPal = SelectPalette(hDC,hPal,FALSE);
  480.         RealizePalette(hDC);
  481.  
  482.         SetWindowExtEx(hDC,nSize,nSize,NULL);
  483.         SetViewportExtEx(hDC,rect.right,-rect.bottom,NULL);
  484.         SetViewportOrgEx(hDC,0,rect.bottom,NULL);
  485.  
  486.         nLoop = nSize >> 1;
  487.         for(idx=0; idx < nLoop; idx++)
  488.         {
  489.           hBrush = CreateSolidBrush(PALETTEINDEX(idx+nLoop));
  490.           SetRect(&rect,idx,idx,nSize-idx,nSize-idx);
  491.           FillRect(hDC,&rect,hBrush);
  492.           DeleteObject(hBrush);
  493.         }
  494.         DeleteObject(SelectPalette(hDC,hPal,FALSE));
  495.         RealizePalette(hDC);
  496.       }
  497.     }
  498.     else
  499.     {
  500.       SetWindowExtEx(hDC,512,512,NULL);
  501.       SetViewportExtEx(hDC,rect.right,-rect.bottom,NULL);
  502.       SetViewportOrgEx(hDC,0,rect.bottom,NULL);
  503.  
  504.       for(idx=0; idx < 256; idx++)
  505.       {
  506.         hBrush = CreateSolidBrush(RGB(0,0,idx));
  507.         SetRect(&rect,idx,idx,512-idx,512-idx);
  508.         FillRect(hDC,&rect,hBrush);
  509.         DeleteObject(hBrush);
  510.       }
  511.     }
  512.  
  513.     SetMapMode(hDC,nMapMode);
  514.  
  515.     EndPaint(hWnd,&ps);
  516.   }
  517.  
  518.   return;
  519. }
  520.  
  521.  
  522. /*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
  523.   Function: SkipAnsi
  524.  
  525.   Summary:  Utility function to scan an input ANSI string and either skips
  526.             white characters or skips non-white characters.
  527.  
  528.   Args;     LPSTR psz,
  529.               Input ANSI string to be scanned.
  530.             BOOL bSkip)
  531.               Input boolean determining whether to skip white space
  532.               or not.  TRUE means skip white space; FALSE means skip
  533.               non-white chars.
  534.  
  535.   Returns:  LPSTR
  536.               String pointer after the skip.
  537. F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
  538. LPSTR SkipAnsi(
  539.          LPSTR psz,
  540.          BOOL bSkipWhite)
  541. {
  542.   char ch;
  543.   BOOL bWhite;
  544.  
  545.   while (ch = *psz)
  546.   {
  547.     bWhite = ('\n'==ch || '\r'==ch || '\t'==ch || ' '==ch);
  548.  
  549.     if ((bSkipWhite && !bWhite) || (!bSkipWhite && bWhite))
  550.       break;
  551.  
  552.     psz++;
  553.   }
  554.  
  555.   return psz;
  556. }
  557.  
  558.  
  559. /*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
  560.   Function: FileExist
  561.  
  562.   Summary:  Function to test for the existance of a file.
  563.  
  564.   Args:     TCHAR* pszFileName
  565.               String pointer to file's path/name.
  566.  
  567.   Returns:  BOOL.  TRUE if file exists; FALSE if not.
  568. F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
  569. BOOL FileExist(TCHAR* pszFileName)
  570. {
  571.   BOOL bExist = FALSE;
  572.   HANDLE hFile;
  573.  
  574.   if (NULL != pszFileName)
  575.   {
  576.     // Use the preferred Win32 API call and not the older OpenFile.
  577.     hFile = CreateFile(
  578.               pszFileName,
  579.               GENERIC_READ,
  580.               FILE_SHARE_READ | FILE_SHARE_WRITE,
  581.               NULL,
  582.               OPEN_EXISTING,
  583.               0,
  584.               0);
  585.  
  586.     if (hFile != INVALID_HANDLE_VALUE)
  587.     {
  588.       // If the handle is valid then the file exists.
  589.       CloseHandle(hFile);
  590.       bExist = TRUE;
  591.     }
  592.   }
  593.  
  594.   return (bExist);
  595. }
  596.  
  597.  
  598. /*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
  599.   Function: MakeFamilyPath
  600.  
  601.   Summary:  Function to make a family file/path/name string using the
  602.             detetected pathname of the current executable module.
  603.             Makes paths like d:\dir1\dir2\mymodule.hlp,
  604.             d:\dir1\dir2\mymodule.lic, etc.
  605.  
  606.   Args:     HINSTANCE hInst
  607.               Handle to the module intstance.
  608.             TCHAR* pszNewPath
  609.               String pointer to the new path/name.
  610.             TCHAR* pszFileExt
  611.               String pointer to the filename extension for the new path.
  612.  
  613.   Returns:  BOOL.  TRUE if success; FALSE if not.
  614. F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
  615. BOOL MakeFamilyPath(
  616.        HINSTANCE hInst,
  617.        TCHAR* pszNewPath,
  618.        TCHAR* pszFileExt)
  619. {
  620.   BOOL bOk = FALSE;
  621.   TCHAR  szPath[MAX_PATH];
  622.   TCHAR  szDrive[64];
  623.   TCHAR  szDir[MAX_PATH];
  624.   TCHAR  szName[64];
  625.  
  626.   if (NULL != pszNewPath)
  627.   {
  628.     pszNewPath[0] = 0;
  629.  
  630.     bOk = (0 != GetModuleFileName(hInst, szPath, MAX_PATH));
  631.     if (bOk)
  632.     {
  633.       _tsplitpath(szPath, szDrive, szDir, szName, NULL);
  634.       lstrcpy(pszNewPath, szDrive);
  635.       lstrcat(pszNewPath, szDir);
  636.       lstrcat(pszNewPath, szName);
  637.       lstrcat(pszNewPath, pszFileExt);
  638.     }
  639.   }
  640.  
  641.   return bOk;
  642. }
  643.  
  644.  
  645. /*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
  646.   Function: GetExeName
  647.  
  648.   Summary:  Function to get the executable file name of the current
  649.             module. Makes names like: MyApp.EXE or MyDll.DLL.
  650.  
  651.   Args:     HINSTANCE hInst
  652.               Handle to the module intstance.
  653.             TCHAR* pszExeName
  654.               String pointer to the new executable file name.
  655.               A char string allocated by user.
  656.  
  657.   Returns:  BOOL.  TRUE if success; FALSE if not.
  658. F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
  659. BOOL GetExeName(
  660.        HINSTANCE hInst,
  661.        TCHAR* pszExeName)
  662. {
  663.   BOOL bOk = FALSE;
  664.   TCHAR  szPath[MAX_PATH];
  665.   TCHAR  szName[64];
  666.   TCHAR  szFileExt[64];
  667.  
  668.   if (NULL != pszExeName)
  669.   {
  670.     pszExeName[0] = 0;
  671.  
  672.     bOk = (0 != GetModuleFileName(hInst, szPath, MAX_PATH));
  673.     if (bOk)
  674.     {
  675.       _tsplitpath(szPath, NULL, NULL, szName, szFileExt);
  676.       lstrcpy(pszExeName, szName);
  677.       lstrcat(pszExeName, szFileExt);
  678.     }
  679.   }
  680.  
  681.   return bOk;
  682. }
  683.  
  684.  
  685. /*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
  686.   Function: CmdExec
  687.  
  688.   Summary:  Execute an EXE Win32 program by creating a process and
  689.             running the specified EXE in it.
  690.  
  691.   Args:     LPTSTR szCmd,
  692.               Entire command line (eg, "notepad.exe mytext.txt")
  693.  
  694.   Returns:  BOOL
  695.               TRUE if succeed; FALSE if fail.
  696. F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
  697. BOOL CmdExec(
  698.        LPTSTR szCmd)
  699. {
  700.   BOOL bOk;
  701.   STARTUPINFO si;
  702.   PROCESS_INFORMATION pi;
  703.  
  704.   // Execute the command with a call to the CreateProcess API call.
  705.   memset(&si,0,sizeof(STARTUPINFO));
  706.   si.cb = sizeof(STARTUPINFO);
  707.   si.wShowWindow = SW_SHOW;
  708.   bOk = CreateProcess(NULL,szCmd,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi);
  709.   CloseHandle(pi.hThread);
  710.   CloseHandle(pi.hProcess);
  711.  
  712.   return bOk;
  713. }
  714.  
  715.  
  716. /*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
  717.   Function: GetErrorMsg
  718.  
  719.   Summary:  Accepts a Win32 error code and retrieves a human readable
  720.             system message for it.
  721.  
  722.   Args:     HRESULT hr
  723.               SCODE error code.
  724.             LPTSTR pszMsg
  725.               Pointer string where message will be placed.
  726.             UINT uiSize
  727.               Max size of the msg string.
  728.  
  729.   Returns:  BOOL
  730.               TRUE if hr was error; FALSE if not.
  731. F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
  732. BOOL GetErrorMsg(
  733.        HRESULT hr,
  734.        LPTSTR pszMsg,
  735.        UINT uiSize)
  736. {
  737.   BOOL bErr = FAILED(hr);
  738.   DWORD dwSize;
  739.  
  740.   if (bErr)
  741.   {
  742.     memset(pszMsg, 0, uiSize * sizeof(TCHAR));
  743.  
  744.     dwSize = FormatMessage(
  745.       FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
  746.       NULL,
  747.       hr,
  748.       MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
  749.       pszMsg,
  750.       uiSize,
  751.       NULL);
  752.     if (dwSize>2)
  753.     {
  754.       // Take out the trailing CRLF.
  755.       pszMsg[--dwSize] = 0;
  756.       pszMsg[--dwSize] = 0;
  757.     }
  758.   }
  759.  
  760.   return bErr;
  761. }
  762.  
  763.  
  764. /*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
  765.   Function: HrMsg
  766.  
  767.   Summary:  HRESULT Error Message box. Takes standard result code,
  768.             looks it up in the system tables, and shows a message
  769.             box with the error code (in hex) and the associated
  770.             system message.
  771.  
  772.   Args:     HWND hWndOwner,
  773.               Handle to owner parent window.
  774.             LPTSTR pszTitle
  775.               User message string (eg, designating the attempted function).
  776.               Appears in dialog title bar.
  777.             HRESULT hr,
  778.               Standard result code.
  779.  
  780.   Returns:  void
  781. F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
  782. void HrMsg(
  783.        HWND hWndOwner,
  784.        LPTSTR pszTitle,
  785.        HRESULT hr)
  786. {
  787.   TCHAR szMsg[MAX_PATH];
  788.   TCHAR szErrMsg[MAX_PATH];
  789.   int iResult;
  790.  
  791.   wsprintf(szMsg, TEXT("Error=0x%X:\r\n"), hr);
  792.   GetErrorMsg(hr, szErrMsg, MAX_PATH);
  793.   lstrcat(szMsg, szErrMsg);
  794.   iResult = MessageBox(
  795.               hWndOwner,
  796.               szMsg,
  797.               pszTitle,
  798.               MB_OK | MB_ICONEXCLAMATION);
  799.  
  800.   return;
  801. }
  802.  
  803.  
  804. /*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
  805.   Function: HrMsgId
  806.  
  807.   Summary:  HRESULT Error Message box using string resource ID. Takes
  808.             standard result code, looks it up in the system tables, and
  809.             shows a message box with the error code (in hex) and the
  810.             associated system message. An additional user message string
  811.             (specified as a resource ID) is also shown.
  812.  
  813.   Args:     HINSTANCE hInst,
  814.               Handle of the module instance.  Needed to specify the
  815.               module instance for fetching the specified string resource.
  816.             HWND hwndOwner,
  817.               Handle of the owner window. NULL for desktop.
  818.             INT iStringID
  819.               Resource ID of user error message string. Appears in dialog
  820.               title bar.
  821.             HRESULT hr,
  822.               Standard result code.
  823.  
  824.   Returns:  void
  825. F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
  826. void HrMsgId(
  827.       HINSTANCE hInst,
  828.       HWND hWndOwner,
  829.       INT iStringID,
  830.       HRESULT hr)
  831. {
  832.   TCHAR szMsg[MAX_STRING_LENGTH];
  833.  
  834.   // Load the error message string from the resources.
  835.   if (LoadString(
  836.         hInst,
  837.         iStringID,
  838.         szMsg,
  839.         MAX_STRING_LENGTH))
  840.   {
  841.     // Put up error message box with the retrieved error string message.
  842.     HrMsg(hWndOwner, szMsg, hr);
  843.   }
  844.  
  845.   return;
  846. }
  847.  
  848.  
  849. /*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
  850.   Function: ReadHelp
  851.  
  852.   Summary:  The ReadHelp function is a general help reader for
  853.             applications. It uses the shell to run the reader application
  854.             associated with the extension of the specified file/path.
  855.             For example, if the family-named file name is MYAPP.HTM, then
  856.             the web browser will be used to read the HTML help file.
  857.  
  858.   Args:     HWND hWndOwner,
  859.               Handle to owner parent window.
  860.             LPTSTR pszHelpFile,
  861.               Pointer to the help file/path name string.
  862.  
  863.   Returns:  void
  864. F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
  865. void ReadHelp(
  866.        HWND hWndOwner,
  867.        LPTSTR pszHelpFile)
  868. {
  869.   int iRes;
  870.  
  871.   if (NULL != pszHelpFile)
  872.   {
  873.     // First check if the .HTM help file is there at all.
  874.     if (FileExist(pszHelpFile))
  875.     {
  876.       // Use shell to invoke web browser on the HTML help file.
  877.       iRes = (int) ShellExecute(
  878.                      hWndOwner,
  879.                      TEXT("open"),
  880.                      pszHelpFile,
  881.                      NULL,
  882.                      NULL,
  883.                      SW_SHOWNORMAL);
  884.       if (iRes <= 32)
  885.       {
  886.         // If unable to browse then put up an error box.
  887.         HrMsg(hWndOwner, TEXT(NOBROWSE_ERROR_STR), E_FAIL);
  888.       }
  889.     }
  890.     else
  891.     {
  892.       // If the .HTM file doesn't exist the put up an error box.
  893.       iRes = MessageBox(
  894.                hWndOwner,
  895.                TEXT(NOHTM_ERROR_STR),
  896.                TEXT(ERROR_TITLE_STR),
  897.                MB_OK | MB_ICONEXCLAMATION);
  898.     }
  899.   }
  900.  
  901.   return;
  902. }
  903.  
  904.  
  905. /*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
  906.   Function: ReadTutorial
  907.  
  908.   Summary:  The ReadTutorial function is for use by tutorial code sample
  909.             applications that study themselves. This function uses the
  910.             Win32 ShellExecute function to launch the web browser to read
  911.             an HTML web page that contains a tutorial covering the sample.
  912.             If no HTM file (in a sibling or parent directory) is
  913.             specified, this function assumes a family-named <sample>.HTM
  914.             tutorial HTML file that is located next to the main .EXE file
  915.             of the application calling ReadTutorial. "Family-named" means
  916.             that if the EXE program calling this function is MYAPP.EXE
  917.             then the associated family-named HTML file is constructed as
  918.             MYAPP.HTM.
  919.  
  920.   Args:     HINSTANCE hInst,
  921.               Handle of the executable module instance.
  922.             HWND hWndOwner,
  923.               Handle to owner parent window.
  924.             LPTSTR pszHTMLFile,
  925.               Pointer to an optional HTML FileName string. If this pointer
  926.               is NULL, then the family-named file is assumed for the HTML
  927.               file and is assumed to be located next to the main .EXE file
  928.               of the program executing this function. Non-NULL values are
  929.               of the form "MySiblingDirectory\MyHTMLFile.HTM" or of the
  930.               form "MyHTMLFileInTheParentDirectory.HTM". Special non-NULL
  931.               values beginning with '.' (eg. ".HTM" or ".ASP") specify
  932.               that the family-named file is assumed to be located in the
  933.               parent directory. In this case, the specified file extension
  934.               is used.
  935.  
  936.   Returns:  void
  937. F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
  938. void ReadTutorial(
  939.        HINSTANCE hInst,
  940.        HWND hWndOwner,
  941.        LPTSTR pszHTMLFile)
  942. {
  943.   TCHAR szFileName[MAX_PATH];
  944.  
  945.   szFileName[0] = 0;
  946.  
  947.   if (NULL == pszHTMLFile)
  948.   {
  949.     // Build a path to where the .HTM file should be. It should be in
  950.     // the same directory as the .EXE but with the .HTM extension.
  951.     MakeFamilyPath(hInst, szFileName, TEXT(HTML_FILE_EXT));
  952.   }
  953.   else
  954.   {
  955.     // Use the HTML file specified in a sibling directory.
  956.     BOOL bOk = FALSE;
  957.     TCHAR  szPath[MAX_PATH];
  958.     TCHAR  szDrive[64];
  959.     TCHAR  szDir[MAX_PATH];
  960.     TCHAR  szName[64];
  961.     LPTSTR psz;
  962.     LONG   len;
  963.  
  964.     bOk = (0 != GetModuleFileName(hInst, szPath, MAX_PATH));
  965.     if (bOk)
  966.     {
  967.       _tsplitpath(szPath, szDrive, szDir, szName, NULL);
  968.       len = lstrlen(szDir);
  969.       if (len > 1)
  970.       {
  971.         // Scan backwards in the Dir path to obtain the path to the
  972.         // parent directory.
  973.         psz = &szDir[0] + len - 1;
  974.         while (psz > &szDir[0])
  975.         {
  976.           psz--;
  977.           if (*psz == '\\' || *psz == '/')
  978.           {
  979.             psz++;
  980.             *psz = 0;
  981.             break;
  982.           }
  983.         }
  984.         // Now build the complete Path & FileName using the Specified
  985.         // HTML file name.
  986.         lstrcpy(szFileName, szDrive);
  987.         lstrcat(szFileName, szDir);
  988.         if ('.' == *pszHTMLFile)
  989.           lstrcat(szFileName, szName);
  990.         lstrcat(szFileName, pszHTMLFile);
  991.       }
  992.     }
  993.   }
  994.  
  995.   // Now Browse/Read the HTM file.
  996.   ReadHelp(hWndOwner, szFileName);
  997.  
  998.   return;
  999. }
  1000.  
  1001.  
  1002. /*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
  1003.   Function: GoWeb
  1004.  
  1005.   Summary:  General utility function to launch a web browser on a
  1006.             specified HTML file via either a URL or a full file path/name.
  1007.  
  1008.   Args:     HINSTANCE hInst,
  1009.               Handle of the executable module instance.
  1010.             LPSTR pszTarget
  1011.               Pointer to the Target specification string (must be ANSI).
  1012.               If null then call the ReadTutorial function. If pszTarget
  1013.               starts with '//' then the target is assumed to be a www URL
  1014.               trailer of the form: //www.microsoft.com. If any other
  1015.               string, then it is assumed to be a full path/file name.
  1016.  
  1017.   Returns:  void
  1018. F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
  1019. void GoWeb(
  1020.        HINSTANCE hInst,
  1021.        LPSTR pszTarget)
  1022. {
  1023.   int iRes;
  1024.   BOOL bOk = FALSE;
  1025.   LPTSTR pszNew;
  1026.   TCHAR  szNew[MAX_PATH];
  1027.   TCHAR  szWork[MAX_PATH];
  1028.  
  1029.   szNew[0] = 0;
  1030.   pszNew = (LPTSTR) &szNew[0];
  1031.  
  1032. #if defined(UNICODE)
  1033.   AnsiToUc(pszTarget, pszNew, MAX_PATH);
  1034. #else
  1035.   lstrcpy(pszNew, pszTarget);
  1036. #endif
  1037.  
  1038.   if (NULL == pszTarget)
  1039.     ReadTutorial(hInst, NULL, NULL);
  1040.   else if ('/' == *pszNew)
  1041.   {
  1042.     lstrcpy(szWork, TEXT("http:"));
  1043.     lstrcat(szWork, pszNew);
  1044.     pszNew = szWork;
  1045.     bOk = TRUE;
  1046.   }
  1047.   else
  1048.   {
  1049.     // First check if the .HTM file is there at all.
  1050.     bOk = FileExist(pszNew);
  1051.     if (!bOk)
  1052.     {
  1053.       // If the .HTM file doesn't exist the put up an error box.
  1054.       iRes = MessageBox(
  1055.                NULL,
  1056.                TEXT(NOHTM_ERROR_STR),
  1057.                TEXT(ERROR_TITLE_STR),
  1058.                MB_OK | MB_ICONEXCLAMATION);
  1059.     }
  1060.   }
  1061.  
  1062.   if (bOk)
  1063.   {
  1064.     // Use shell to invoke web browser on the readme HTML file.
  1065.     iRes = (int) ShellExecute(NULL, NULL, pszNew, NULL, NULL, 0);
  1066.     if (iRes <= 32)
  1067.     {
  1068.       // If unable to browse then put up an error box.
  1069.       HrMsg(NULL, TEXT(NOBROWSE_ERROR_STR), E_FAIL);
  1070.     }
  1071.   }
  1072.  
  1073.   return;
  1074. }
  1075.  
  1076.  
  1077. /*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
  1078.   Function: ReadSource
  1079.  
  1080.   Summary:  For use by code sample applications that study themselves
  1081.             as it were.  This function allows you to select one of the
  1082.             source files of this code sample and launch a reader to
  1083.             edit/read it.  NOTEPAD.EXE is the default editor/reader.
  1084.             You can change this by changing EDITOR_FILE_STR in APPUTIL.H.
  1085.  
  1086.   Args:     HWND hWndOwner
  1087.               Handle of parent window.
  1088.             OPENFILENAME* pofn,
  1089.               Pointer to the Open File Name Common Dialog structure.
  1090.  
  1091.   Returns:  void
  1092. F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
  1093. void ReadSource(
  1094.        HWND hWndOwner,
  1095.        OPENFILENAME* pOfn)
  1096. {
  1097.   TCHAR szReadCmd[MAX_PATH+12];
  1098.   TCHAR szFileName[MAX_PATH];
  1099.   BOOL bOk;
  1100.   int iResult;
  1101.  
  1102.   // We'll use the OpenFileName Common Dialog (defined in main app).
  1103.   // Set the dialog's file filter and title.
  1104.   szFileName[0] = 0;
  1105.   pOfn->lpstrFilter = TEXT(OFN_SOURCEFILES_STR);
  1106.   pOfn->lpstrTitle = TEXT(OFN_SOURCETITLE_STR);
  1107.   pOfn->lpstrFile = szFileName;
  1108.  
  1109.   // Call up the dialog to get a file name from the user.
  1110.   if (GetOpenFileName(pOfn))
  1111.   {
  1112.     // If the user provided a file name then assemble a command string
  1113.     lstrcpy(szReadCmd, TEXT(EDITOR_EXE_STR));
  1114.     lstrcat(szReadCmd, szFileName);
  1115.     // And execute it.
  1116.     bOk = CmdExec(szReadCmd);
  1117.     if (!bOk)
  1118.     {
  1119.       // If create procees failed then put up an error box.
  1120.       iResult = MessageBox(
  1121.                   hWndOwner,
  1122.                   TEXT(NOEDITOR_ERROR_STR),
  1123.                   TEXT(ERROR_TITLE_STR),
  1124.                   MB_OK | MB_ICONEXCLAMATION);
  1125.     }
  1126.   }
  1127.  
  1128.   return;
  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: OutputDebugFmt
  1134.  
  1135.   Summary:  Wraps the Win32 OutputDebugString API call to provide
  1136.             printf-style formatted (and variable argument) output.
  1137.  
  1138.   Args:     LPTSTR pszFmt,
  1139.               Format string.
  1140.             [...]
  1141.               Arguments to match those specified in the format string.
  1142.  
  1143.   Returns:  void
  1144. F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
  1145. void OutputDebugFmt(
  1146.        LPTSTR pszFmt,
  1147.        ...)
  1148. {
  1149.   va_list arglist;
  1150.   va_start(arglist, pszFmt);
  1151.   TCHAR szMsg[MAX_STRING_LENGTH];
  1152.  
  1153.   // Use the format string and arguments to format the content text.
  1154.   wvsprintf(szMsg, pszFmt, arglist);
  1155.   // Output the newly formated message string to the debugger display.
  1156.   OutputDebugString(szMsg);
  1157.  
  1158.   return;
  1159. }
  1160.  
  1161.  
  1162. /*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
  1163.   Function: WindowProc
  1164.  
  1165.   Summary:  Standard WindowProc callback function that forwards Windows
  1166.             messages on to the CVirWindow::WindowProc method.  This
  1167.             Window procedure expects that it will receive a "this"
  1168.             pointer as the lpCreateParams member passed as part of the
  1169.             WM_NCCREATE message.  It saves the "this" pointer in the
  1170.             GWL_USERDATA field of the window structure.
  1171.  
  1172.   Args:     HWND hWnd,
  1173.               Window handle.
  1174.             UINT uMsg,
  1175.               Windows message.
  1176.             WPARAM wParam,
  1177.               First message parameter (word sized).
  1178.             LPARAM lParam);
  1179.               Second message parameter (long sized).
  1180.  
  1181.   Returns:  LRESULT.  Windows window procedure callback return value.
  1182. F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
  1183. LRESULT CALLBACK WindowProc(
  1184.                    HWND hWnd,
  1185.                    UINT uMsg,
  1186.                    WPARAM wParam,
  1187.                    LPARAM lParam)
  1188. {
  1189.   // Get a pointer to the window class object.
  1190.   CVirWindow* pWin = (CVirWindow*) GetWindowLong(hWnd, GWL_USERDATA);
  1191.  
  1192.   switch (uMsg)
  1193.   {
  1194.     case WM_NCCREATE:
  1195.       // Since this is the first time that we can get ahold of
  1196.       // a pointer to the window class object, all messages that might
  1197.       // have been sent before this are never seen by the Windows object
  1198.       // and only get passed on to the DefWindowProc
  1199.  
  1200.       // Get the initial creation pointer to the window object
  1201.       pWin = (CVirWindow *) ((CREATESTRUCT *)lParam)->lpCreateParams;
  1202.  
  1203.       // Set it's protected m_hWnd member variable to ensure that
  1204.       // member functions have access to the correct window handle.
  1205.       pWin->m_hWnd = hWnd;
  1206.  
  1207.       // Set its USERDATA DWORD to point to the window object
  1208.       SetWindowLong(hWnd, GWL_USERDATA, (long) pWin);
  1209.       break;
  1210.  
  1211.     case WM_DESTROY:
  1212.       // This is our signal to destroy the window object.
  1213.       SetWindowLong(hWnd, GWL_USERDATA, 0);
  1214.       DELETE_POINTER(pWin);
  1215.       break;
  1216.  
  1217.     default:
  1218.       break;
  1219.   }
  1220.  
  1221.   // Call its message proc method.
  1222.   if (NULL != pWin)
  1223.     return (pWin->WindowProc(uMsg, wParam, lParam));
  1224.   else
  1225.     return (DefWindowProc(hWnd, uMsg, wParam, lParam));
  1226. }
  1227.  
  1228.  
  1229. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1230.   Method:   CVirWindow::Create
  1231.  
  1232.   Summary:  Envelopes the Windows' CreateWindow call.  Uses its
  1233.             window-creation data pointer to pass the 'this' pointer.
  1234.  
  1235.   Args:     LPTSTR lpszClassName,
  1236.               Address of registered class name.
  1237.             LPTSTR lpszWindowName,
  1238.               Address of window name/title.
  1239.             DWORD dwStyle,
  1240.               Window style.
  1241.             int x,
  1242.               Horizontal position of window.
  1243.             int y,
  1244.               Vertical position of window.
  1245.             int nWidth,
  1246.               Window width.
  1247.             int nHeight,
  1248.               Window height.
  1249.             HWND hwndParent,
  1250.               Handle of parent or owner window.
  1251.             HMENU hmenu,
  1252.               Handle of menu, or child window identifier.
  1253.             HINSTANCE hinst)
  1254.               Handle of application instance.
  1255.  
  1256.   Modifies: m_hWnd, m_hInst.
  1257.  
  1258.   Returns:  HWND (Window handle) of the created window.
  1259. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1260. HWND CVirWindow::Create(
  1261.        LPTSTR lpszClassName,
  1262.        LPTSTR lpszWindowName,
  1263.        DWORD dwStyle,
  1264.        int x,
  1265.        int y,
  1266.        int nWidth,
  1267.        int nHeight,
  1268.        HWND hWndParent,
  1269.        HMENU hMenu,
  1270.        HINSTANCE hInst)
  1271. {
  1272.   // Remember the passed instance handle in a member variable of the
  1273.   // C++ Window object.
  1274.   m_hInst = hInst;
  1275.  
  1276.   // Call the Win32 API to create the window.
  1277.   m_hWnd = ::CreateWindow(
  1278.                lpszClassName,
  1279.                lpszWindowName,
  1280.                dwStyle,
  1281.                x,
  1282.                y,
  1283.                nWidth,
  1284.                nHeight,
  1285.                hWndParent,
  1286.                hMenu,
  1287.                hInst,
  1288.                this);
  1289.  
  1290.   return (m_hWnd);
  1291. }
  1292.  
  1293.  
  1294. /*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
  1295.   Function: DialogProc
  1296.  
  1297.   Summary:  The general dialog procedure callback function.  Used by all
  1298.             CVirDialog class objects.  This procedure is the DialogProc
  1299.             registered for all dialogs created with the CVirDialog class.
  1300.             It uses the parameter passed with the WM_INITDIALOG message
  1301.             to identify the dialog classes' "this" pointer which it then
  1302.             stores in the window structure's GWL_USERDATA field.
  1303.             All subsequent messages can then be forwarded on to the
  1304.             correct dialog class's DialogProc method by using the pointer
  1305.             stored in the GWL_USERDATA field.
  1306.  
  1307.   Args:     HWND hWndDlg,
  1308.               Handle of dialog box.
  1309.             UINT uMsg,
  1310.               Message.
  1311.             WPARAM wParam,
  1312.               First message parameter (word sized).
  1313.             LPARAM lParam);
  1314.               Second message parameter (long sized).
  1315.  
  1316.   Returns:  BOOL.  Return of the CVirDialog::DialogProc method.
  1317. F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
  1318. BOOL CALLBACK DialogProc(
  1319.                 HWND hWndDlg,
  1320.                 UINT uMsg,
  1321.                 WPARAM wParam,
  1322.                 LPARAM lParam)
  1323. {
  1324.   // Get a pointer to the window class object.
  1325.   CVirDialog* pdlg = (CVirDialog*) GetWindowLong(hWndDlg, GWL_USERDATA);
  1326.  
  1327.   switch (uMsg)
  1328.   {
  1329.     case WM_INITDIALOG:
  1330.       // Get a pointer to the window class object.
  1331.       pdlg = (CVirDialog*) lParam;
  1332.  
  1333.       // Assign the m_hWnd member variable.
  1334.       pdlg->m_hWnd = hWndDlg;
  1335.  
  1336.       // Set the USERDATA word to point to the class object.
  1337.       SetWindowLong(hWndDlg, GWL_USERDATA, (long) pdlg);
  1338.       break;
  1339.  
  1340.     default:
  1341.       break;
  1342.   }
  1343.  
  1344.   // Call its message proc method.
  1345.   if (pdlg != (CVirDialog *) 0)
  1346.     return (pdlg->DialogProc(hWndDlg, uMsg, wParam, lParam));
  1347.   else
  1348.     return (FALSE);
  1349. }
  1350.  
  1351.  
  1352. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1353.   Method:   CVirDialog::ShowDialog
  1354.  
  1355.   Summary:  Creates the dialog so that it's DialogProc member function can
  1356.             be invoked.  The dialog box object exists until deleted by the
  1357.             caller.  It can be shown any number of times.  This function is
  1358.             analgous to Windows' DialogBox function.  The main difference
  1359.             being that you don't specify a DialogProc; you override the
  1360.             pure virtal function CVirDialog::DialogProc.
  1361.  
  1362.   Args:     HINSTANCE hInst,
  1363.               Handle of the module instance.  Needed to specify the
  1364.               module instance for fetching the dialog template resource
  1365.               (ie, from either a host EXE or DLL).
  1366.             LPTSTR lpszTemplate,
  1367.               Identifies the dialog box template.
  1368.             HWND hwndOwner)
  1369.               Handle of the owner window.
  1370.  
  1371.   Modifies: m_hInst.
  1372.  
  1373.   Returns:  Return value from the DialogBoxParam Windows API function.
  1374. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1375. int CVirDialog::ShowDialog(
  1376.       HINSTANCE hInst,
  1377.       LPTSTR lpszTemplate,
  1378.       HWND hWndOwner)
  1379. {
  1380.   int iResult;
  1381.  
  1382.   // Assign the module instance handle in the Dialog object.
  1383.   m_hInst = hInst;
  1384.  
  1385.   // Create and show the dialog on screen.  Load the dialog resource
  1386.   // from the specified module instance (could be a module other than
  1387.   // that of the EXE that is running--the resources could be in a DLL
  1388.   // that is calling this ShowDialog).  Pass the 'this' pointer to the
  1389.   // dialog object so that it can be assigned inside the dialog object
  1390.   // during WM_INITDIALOG and later available to the dailog procedure
  1391.   // via the GWL_USERDATA associated with the dialog window.
  1392.   iResult = ::DialogBoxParam(
  1393.                 hInst,
  1394.                 lpszTemplate,
  1395.                 hWndOwner,
  1396.                 (DLGPROC)::DialogProc,
  1397.                 (long)this);
  1398.  
  1399.   return (iResult);
  1400. }
  1401.  
  1402.  
  1403. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1404.   Method:   CAboutBox::DialogProc
  1405.  
  1406.   Summary:  Dialog proc for the About dialog box.  This DialogProc
  1407.             method definition overrides the same-named pure virtual
  1408.             function in abstract base class CVirDialog thus giving
  1409.             AboutBox-unique message dispatching behavior to this
  1410.             derivation of CVirDialog.  The remaining behavior of
  1411.             CAboutBox is inherited from CVirDialog and is common to
  1412.             all CVirDialogs.
  1413.  
  1414.   Args:     HWND hWndDlg,
  1415.               Handle to the dialog.
  1416.             UINT uMsg,
  1417.               Windows message to dialog.
  1418.             WPARAM wParam,
  1419.               First message parameter (word sized).
  1420.             LPARAM lParam)
  1421.               Second message parameter (long sized).
  1422.  
  1423.   Modifies: .
  1424.  
  1425.   Returns:  BOOL.  TRUE if message was handled; FALSE otherwise.
  1426. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1427. BOOL CAboutBox::DialogProc(
  1428.        HWND hWndDlg,
  1429.        UINT uMsg,
  1430.        WPARAM wParam,
  1431.        LPARAM lParam)
  1432. {
  1433.   BOOL bResult = TRUE;
  1434.  
  1435.   switch (uMsg)
  1436.   {
  1437.     case WM_INITDIALOG:
  1438.       SetFocus(GetDlgItem(hWndDlg,IDOK));
  1439.       break;
  1440.  
  1441.     case WM_COMMAND:
  1442.       if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
  1443.         ::EndDialog(hWndDlg, TRUE);
  1444.       break;
  1445.  
  1446.     case WM_PAINT:
  1447.       // Wash the background of the aboutbox to give it a nice
  1448.       // blue-scaling effect.  Invalidate the OK button to force it to the
  1449.       // top.  This seems to be necessary since the OK button gets
  1450.       // overwritten during the washing.
  1451.       PaintWindow(hWndDlg,COLOR_SCALE_BLUE);
  1452.       InvalidateRect(GetDlgItem(hWndDlg,IDOK),NULL,TRUE);
  1453.       break;
  1454.  
  1455.     default:
  1456.       bResult = FALSE;
  1457.       break;
  1458.   }
  1459.  
  1460.   return(bResult);
  1461. }
  1462.  
  1463.  
  1464. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1465.   Method:   CDelayBox::DialogProc
  1466.  
  1467.   Summary:  Dialog proc for the Delay dialog box.  This DialogProc method
  1468.             definition overrides the same-named pure virtual function in
  1469.             abstract base class CVirDialog thus giving DelayBox-unique
  1470.             message dispatching behavior to this derivation of CVirDialog.
  1471.             The remaining behavior of CDelayBox is inherited from
  1472.             CVirDialog and is common to all CVirDialogs.
  1473.  
  1474.   Args:     HWND hWndDlg,
  1475.               Handle to the dialog.
  1476.             UINT uMsg,
  1477.               Windows message to dialog.
  1478.             WPARAM wParam,
  1479.               First message parameter (word sized).
  1480.             LPARAM lParam)
  1481.               Second message parameter (long sized).
  1482.  
  1483.   Modifies: .
  1484.  
  1485.   Returns:  BOOL.  TRUE if message was handled; FALSE otherwise.
  1486. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1487. BOOL CDelayBox::DialogProc(
  1488.        HWND hWndDlg,
  1489.        UINT uMsg,
  1490.        WPARAM wParam,
  1491.        LPARAM lParam)
  1492. {
  1493.   BOOL  bResult = TRUE;
  1494.   DWORD nEndCount;
  1495.   TCHAR szPath[MAX_PATH];
  1496.   TCHAR szName[64];
  1497.  
  1498.   switch (uMsg)
  1499.   {
  1500.     case WM_INITDIALOG:
  1501.       break;
  1502.  
  1503.     case WM_PAINT:
  1504.       // Get a title based on this module's name.
  1505.       if (0 != GetModuleFileName(m_hInst, szPath, MAX_PATH))
  1506.       {
  1507.         _tsplitpath(szPath, NULL, NULL, szName, NULL);
  1508.         SetWindowText(hWndDlg, szName);
  1509.       }
  1510.       // Wash the background of the dialog to give it a nice
  1511.       // blue-scaling effect.
  1512.       PaintWindow(hWndDlg, COLOR_SCALE_BLUE);
  1513.       PostMessage(hWndDlg, WM_COMMAND, 0, 0);
  1514.       break;
  1515.  
  1516.     case WM_COMMAND:
  1517.       nEndCount = GetTickCount() + DELAYBOX_DURATION;
  1518.       while (GetTickCount() < nEndCount)
  1519.       { }
  1520.       ::EndDialog(hWndDlg, TRUE);
  1521.       break;
  1522.  
  1523.     default:
  1524.       bResult = FALSE;
  1525.       break;
  1526.   }
  1527.  
  1528.   return(bResult);
  1529. }
  1530.  
  1531.  
  1532. /*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
  1533.   Function: DelayBox
  1534.  
  1535.   Summary:  Uses the specified dialog template from the caller's resources
  1536.             and shows a buttonless dialog message box that remains on
  1537.             screen for about 5 seconds.
  1538.  
  1539.   Args:     HINSTANCE hInst,
  1540.               Handle of the module instance.  Needed to specify the
  1541.               module instance for fetching the dialog template resource
  1542.               (ie, from either a host EXE or DLL).
  1543.             LPTSTR pszTemplate,
  1544.               Identifies the dialog box template.
  1545.             HWND hwndOwner)
  1546.               Handle of the owner window. NULL for desktop.
  1547.  
  1548.   Returns:  void
  1549. F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
  1550. void DelayBox(
  1551.       HINSTANCE hInst,
  1552.       LPTSTR pszTemplate,
  1553.       HWND hWndOwner)
  1554. {
  1555.   CDelayBox dlgBox;
  1556.  
  1557.   // Show a time-delayed message box.
  1558.   dlgBox.ShowDialog(hInst, pszTemplate, hWndOwner);
  1559.  
  1560.   return;
  1561. }
  1562.  
  1563.  
  1564. /*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
  1565.   Function: ErrorBox
  1566.  
  1567.   Summary:  The ErrorBox function displays an error message dialog box. It
  1568.             displays an error message string in the dialog that is
  1569.             specified in the call by a resource ID.
  1570.  
  1571.   Args:     HINSTANCE hInst,
  1572.               Handle of the module instance.  Needed to specify the
  1573.               module instance for fetching the specified string resource.
  1574.             HWND hwndOwner,
  1575.               Handle of the owner window. NULL for desktop.
  1576.             INT iStringID
  1577.               Resource ID of the error message sting.
  1578.  
  1579.   Returns:  void
  1580. F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
  1581. void ErrorBox(
  1582.       HINSTANCE hInst,
  1583.       HWND hWndOwner,
  1584.       INT iStringID)
  1585. {
  1586.   TCHAR szMsg[MAX_STRING_LENGTH];
  1587.  
  1588.   // Load the error message string from the resources.
  1589.   if (LoadString(
  1590.         hInst,
  1591.         iStringID,
  1592.         szMsg,
  1593.         MAX_STRING_LENGTH))
  1594.   {
  1595.     // Put up error message box with the retrieved error string message.
  1596.     MessageBox(
  1597.       hWndOwner,
  1598.       szMsg,
  1599.       TEXT(ERROR_TITLE_STR),
  1600.       MB_OK | MB_ICONEXCLAMATION);
  1601.   }
  1602.  
  1603.   return;
  1604. }
  1605.  
  1606.  
  1607. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1608.   Method:   CMsgBox::Init
  1609.  
  1610.   Summary:  CMsgBox constructor.  Initializes private member data handles
  1611.             for both the eventual Parent window of any MsgBox and the
  1612.             application instance.
  1613.  
  1614.   Args:     HINSTANCE hInst,
  1615.               Handle of app instance.
  1616.             HWND hWndParent)
  1617.               Handle of parent window.
  1618.  
  1619.   Returns:  BOOL
  1620.               TRUE.
  1621. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1622. BOOL CMsgBox::Init(
  1623.       HINSTANCE hInst,
  1624.       HWND hWndOwner)
  1625. {
  1626.   m_hInst = hInst;
  1627.   m_hWndOwner = hWndOwner;
  1628.  
  1629.   return (TRUE);
  1630. }
  1631.  
  1632.  
  1633. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1634.   Method:   CMsgBox::Error
  1635.  
  1636.   Summary:  Shows a specified message string in an Error MessageBox Dialog.
  1637.  
  1638.   Args:     LPTSTR szMsg
  1639.               The message string to display.
  1640.  
  1641.   Returns:  int
  1642.               Result of the MessageBox call.
  1643. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1644. int CMsgBox::Error(
  1645.       LPTSTR szMsg)
  1646. {
  1647.   int iResult;
  1648.  
  1649.   iResult = MessageBox(
  1650.               m_hWndOwner,
  1651.               szMsg,
  1652.               TEXT(ERROR_TITLE_STR),
  1653.               MB_OK | MB_ICONEXCLAMATION);
  1654.  
  1655.   return (iResult);
  1656. }
  1657.  
  1658.  
  1659. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1660.   Method:   CMsgBox::ErrorID
  1661.  
  1662.   Summary:  Shows a resource ID specified message string in an Error
  1663.             MessageBox Dialog.
  1664.  
  1665.   Args:     UINT uMsgID
  1666.               The resource ID of the message string to display.
  1667.  
  1668.   Returns:  int
  1669.               Result of the MessageBox call.
  1670. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1671. int CMsgBox::ErrorID(
  1672.       UINT uMsgID)
  1673. {
  1674.   int iResult = FALSE;
  1675.   TCHAR szMsg[MAX_STRING_LENGTH];
  1676.  
  1677.   if (LoadString(m_hInst, uMsgID, szMsg, MAX_STRING_LENGTH))
  1678.   {
  1679.     iResult = MessageBox(
  1680.                 m_hWndOwner,
  1681.                 szMsg,
  1682.                 TEXT(ERROR_TITLE_STR),
  1683.                 MB_OK | MB_ICONEXCLAMATION);
  1684.   }
  1685.  
  1686.   return (iResult);
  1687. }
  1688.  
  1689.  
  1690. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1691.   Method:   CMsgBox::Note
  1692.  
  1693.   Summary:  Shows a specified message string in a Notice MessageBox Dialog.
  1694.  
  1695.   Args:     LPTSTR szMsg
  1696.               The message string to display.
  1697.  
  1698.   Returns:  int
  1699.               Result of the MessageBox call.
  1700. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1701. int CMsgBox::Note(
  1702.       LPTSTR szMsg)
  1703. {
  1704.   int iResult;
  1705.  
  1706.   iResult = MessageBox(
  1707.               m_hWndOwner,
  1708.               szMsg,
  1709.               TEXT(NOTICE_TITLE_STR),
  1710.               MB_OK | MB_ICONINFORMATION);
  1711.  
  1712.   return (iResult);
  1713. }
  1714.  
  1715.  
  1716. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1717.   Method:   CMsgBox::NoteID
  1718.  
  1719.   Summary:  Shows a resource ID specified message string in a Notice
  1720.             MessageBox Dialog.
  1721.  
  1722.   Args:     UINT uMsgID
  1723.               The resource ID of the message string to display.
  1724.  
  1725.   Returns:  int
  1726.               Result of the MessageBox call.
  1727. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1728. int CMsgBox::NoteID(
  1729.       UINT uMsgID)
  1730. {
  1731.   int iResult = FALSE;
  1732.   TCHAR szMsg[MAX_STRING_LENGTH];
  1733.  
  1734.   if (LoadString(m_hInst, uMsgID, szMsg, MAX_STRING_LENGTH))
  1735.   {
  1736.     iResult = MessageBox(
  1737.                 m_hWndOwner,
  1738.                 szMsg,
  1739.                 TEXT(NOTICE_TITLE_STR),
  1740.                 MB_OK | MB_ICONINFORMATION);
  1741.   }
  1742.  
  1743.   return (iResult);
  1744. }
  1745.  
  1746.  
  1747. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1748.   Method:   CMsgBox::NoteFmt
  1749.  
  1750.   Summary:  Shows a printf-style formatted message string in a Notice
  1751.             MessageBox Dialog.
  1752.  
  1753.   Args:     LPTSTR szFmtMsg
  1754.               The format/message string to display.
  1755.             [...]
  1756.               Arguments to match those specified in the format string.
  1757.  
  1758.   Returns:  int
  1759.               Result of the MessageBox call.
  1760. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1761. int CMsgBox::NoteFmt(
  1762.       LPTSTR szFmtMsg
  1763.            ...)
  1764. {
  1765.   int iResult = 0;
  1766.   va_list arglist;
  1767.   va_start(arglist, szFmtMsg);
  1768.   TCHAR szMsg[MAX_STRING_LENGTH];
  1769.  
  1770.   // Use the format string to format the messagebox's content text.
  1771.   wvsprintf(szMsg, szFmtMsg, arglist);
  1772.   iResult = MessageBox(
  1773.               m_hWndOwner,
  1774.               szMsg,
  1775.               TEXT(NOTICE_TITLE_STR),
  1776.               MB_OK | MB_ICONINFORMATION);
  1777.  
  1778.   return (iResult);
  1779. }
  1780.  
  1781.  
  1782. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1783.   Method:   CMsgBox::NoteFmtID
  1784.  
  1785.   Summary:  Shows a resource ID specified printf-style formatted message
  1786.             string in a Notice MessageBox Dialog.
  1787.  
  1788.   Args:     UINT uFmtMsgID
  1789.               The resource ID of the format message string to display.
  1790.             [...]
  1791.               Arguments to match those specified in the format string.
  1792.  
  1793.   Returns:  int
  1794.               Result of the MessageBox call.
  1795. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1796. int CMsgBox::NoteFmtID(
  1797.       UINT uFmtMsgID,
  1798.       ...)
  1799. {
  1800.   int iResult = 0;
  1801.   va_list arglist;
  1802.   va_start(arglist, uFmtMsgID);
  1803.   TCHAR szFmtMsg[MAX_STRING_LENGTH];
  1804.   TCHAR szMsg[MAX_STRING_LENGTH];
  1805.  
  1806.   if (LoadString(m_hInst, uFmtMsgID, szFmtMsg, MAX_STRING_LENGTH))
  1807.   {
  1808.     // Use the format string to format the messagebox's content text.
  1809.     wvsprintf(szMsg, szFmtMsg, arglist);
  1810.     iResult = MessageBox(
  1811.                 m_hWndOwner,
  1812.                 szMsg,
  1813.                 TEXT(NOTICE_TITLE_STR),
  1814.                 MB_OK | MB_ICONINFORMATION);
  1815.   }
  1816.  
  1817.   return (iResult);
  1818. }
  1819.  
  1820.  
  1821. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1822.   Method:   CMsgLog::Create
  1823.  
  1824.   Summary:  Creates the ListBox as a child window to fill the client area
  1825.             of the specified parent window or to exist as a separate
  1826.             window owned by the parent window.
  1827.  
  1828.   Args:     HINSTANCE hInst,
  1829.               Instance handle of the application.
  1830.             HWND hWndparent,
  1831.               Window handle for the parent window of the listbox.
  1832.             BOOL bChild)
  1833.               Flag to create the listbox as a child window.  TRUE means fit
  1834.               the child window to fill the client area of the parent window.
  1835.               FALSE means the window is a separate (but owned) window.
  1836.  
  1837.   Returns:  BOOL
  1838.               TRUE if successful; FALSE if not.
  1839. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1840. BOOL CMsgLog::Create(
  1841.        HINSTANCE hInst,
  1842.        HWND hWndParent,
  1843.        BOOL bChild)
  1844. {
  1845.   BOOL bResult = FALSE;
  1846.   HWND hWnd;
  1847.   RECT rect;
  1848.   DWORD dwStyle = WS_VISIBLE | WS_VSCROLL | WS_HSCROLL | LBS_NOSEL |
  1849.                     LBS_NOINTEGRALHEIGHT;
  1850.   TCHAR* pszTitle = bChild ? NULL : TEXT("Trace Log");
  1851.  
  1852.   dwStyle |= bChild ? WS_CHILD : WS_OVERLAPPEDWINDOW;
  1853.  
  1854.   // Logging will be flagged as on when creation is successful.
  1855.   m_bLogging = FALSE;
  1856.  
  1857.   if (IsWindow(hWndParent))
  1858.   {
  1859.     GetClientRect(hWndParent, &rect);
  1860.  
  1861.     // Create the child ListBox window and fill the parent window with it.
  1862.     hWnd = ::CreateWindowEx(
  1863.                0,                // Extended Window Style
  1864.                TEXT("LISTBOX"),  // Class Name
  1865.                pszTitle,         // Window Title
  1866.                dwStyle,          // The window style
  1867.                0,                // (x,y)=Upper left of Parent window
  1868.                0,
  1869.                rect.right,       // Width; Fill Client Window
  1870.                rect.bottom,      // Height
  1871.                hWndParent,       // Parent Window Handle
  1872.                0,                // No menu
  1873.                hInst,            // App Instance Handle
  1874.                NULL);            // Window Creation Data
  1875.  
  1876.     if (NULL != hWnd)
  1877.     {
  1878.       // Remember the window handle of this listbox window.
  1879.       m_hWndLog = hWnd;
  1880.       // Remember the instance of this application.
  1881.       m_hInstLog = hInst;
  1882.       // Remember if this is a child window (bChild==TRUE).
  1883.       m_bChild = bChild;
  1884.       // Turn on message logging by default after create.
  1885.       m_bLogging = TRUE;
  1886.       // Return success.
  1887.       bResult = TRUE;
  1888.     }
  1889.   }
  1890.  
  1891.   return (bResult);
  1892. }
  1893.  
  1894.  
  1895. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1896.   Method:   CMsgLog::Logging
  1897.  
  1898.   Summary:  Turns logging of or off.
  1899.  
  1900.   Args:     BOOL bLogging
  1901.               TRUE to turn on; FALSE to turn off.
  1902.  
  1903.   Returns:  BOOL
  1904.               New logging status.
  1905. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1906. BOOL CMsgLog::Logging(
  1907.        BOOL bLogging)
  1908. {
  1909.   BOOL bResult = bLogging;
  1910.  
  1911.   m_bLogging = bLogging;
  1912.  
  1913.   return (bResult);
  1914. }
  1915.  
  1916.  
  1917. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1918.   Method:   CMsgLog::Msg
  1919.  
  1920.   Summary:  Logs a message string as a separate line in the listbox.
  1921.  
  1922.   Args:     LPTSTR szMsg
  1923.               Pointer to String of the message to display/log.
  1924.  
  1925.   Returns:  BOOL
  1926.               TRUE if successful; FALSE if not.
  1927. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1928. BOOL CMsgLog::Msg(
  1929.        LPTSTR szMsg)
  1930. {
  1931.   BOOL bResult = FALSE;
  1932.   int iIndex;
  1933.  
  1934.   if (m_bLogging && IsWindow(m_hWndLog))
  1935.   {
  1936.     ::SendMessage(
  1937.         m_hWndLog,
  1938.         LB_ADDSTRING,
  1939.         0,
  1940.         (LPARAM)szMsg);
  1941.     iIndex = ::SendMessage(
  1942.                  m_hWndLog,
  1943.                  LB_GETCOUNT,
  1944.                  0,
  1945.                  0);
  1946.     if (LB_ERR != iIndex && iIndex > 0)
  1947.     {
  1948.       --iIndex;
  1949.       ::SendMessage(
  1950.           m_hWndLog,
  1951.           LB_SETCURSEL,
  1952.           (WPARAM)iIndex,
  1953.           0);
  1954.     }
  1955.     bResult = TRUE;
  1956.   }
  1957.  
  1958. #if defined(DEBUG)
  1959.   ::OutputDebugString(szMsg);
  1960. #endif
  1961.  
  1962.   return (bResult);
  1963. }
  1964.  
  1965.  
  1966. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1967.   Method:   CMsgLog::MsgFmt
  1968.  
  1969.   Summary:  Logs a printf-style formated message as a separate line in the
  1970.             Message log listbox.
  1971.  
  1972.   Args:     LPTSTR szFmtMsg
  1973.               The format/message string to display/log.
  1974.             [...]
  1975.               Arguments to match those specified in the format string.
  1976.  
  1977.   Returns:  BOOL
  1978.               TRUE if successful; FALSE if not.
  1979. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1980. BOOL CMsgLog::MsgFmt(
  1981.        LPTSTR szFmtMsg,
  1982.        ...)
  1983. {
  1984.   BOOL bResult = FALSE;
  1985.   va_list arglist;
  1986.   va_start(arglist, szFmtMsg);
  1987.   TCHAR szMsg[MAX_STRING_LENGTH];
  1988.   int iIndex;
  1989.  
  1990.   // Use the format string and arguments to format the content text.
  1991.   wvsprintf(szMsg, szFmtMsg, arglist);
  1992.  
  1993.   // Send the newly formated message string to the Log Listbox.
  1994.   if (m_bLogging && IsWindow(m_hWndLog))
  1995.   {
  1996.     ::SendMessage(
  1997.         m_hWndLog,
  1998.         LB_ADDSTRING,
  1999.         0,
  2000.         (LPARAM)szMsg);
  2001.     iIndex = ::SendMessage(
  2002.                  m_hWndLog,
  2003.                  LB_GETCOUNT,
  2004.                  0,
  2005.                  0);
  2006.     if (LB_ERR != iIndex && iIndex > 0)
  2007.     {
  2008.       --iIndex;
  2009.       ::SendMessage(
  2010.           m_hWndLog,
  2011.           LB_SETCURSEL,
  2012.           (WPARAM)iIndex,
  2013.           0);
  2014.     }
  2015.     bResult = TRUE;
  2016.   }
  2017.  
  2018. #if defined(DEBUG)
  2019.   ::OutputDebugString(szMsg);
  2020. #endif
  2021.  
  2022.   return (bResult);
  2023. }
  2024.  
  2025.  
  2026. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  2027.   Method:   CMsgLog::MsgID
  2028.  
  2029.   Summary:  Logs a message string as a separate line in the listbox.
  2030.  
  2031.   Args:     int iMsgID
  2032.               String Resource ID for the message to display/log.
  2033.  
  2034.   Returns:  BOOL
  2035.               TRUE if successful; FALSE if not.
  2036. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  2037. BOOL CMsgLog::MsgID(
  2038.        int iMsgID)
  2039. {
  2040.   BOOL bResult = FALSE;
  2041.   TCHAR szMsg[MAX_STRING_LENGTH];
  2042.  
  2043.   if (LoadString(m_hInstLog, iMsgID, szMsg, MAX_STRING_LENGTH))
  2044.     bResult = Msg(szMsg);
  2045.  
  2046.   return (bResult);
  2047. }
  2048.  
  2049.  
  2050. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  2051.   Method:   CMsgLog::MsgFmtID
  2052.  
  2053.   Summary:  Logs a printf-style formated message as a separate line in the
  2054.             Message log listbox.
  2055.  
  2056.   Args:     int iFmtID
  2057.               The resource ID for the format/message string to display/log.
  2058.             [...]
  2059.               Arguments to match those specified in the format string.
  2060.  
  2061.   Returns:  BOOL
  2062.               TRUE if successful; FALSE if not.
  2063. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  2064. BOOL CMsgLog::MsgFmtID(
  2065.        int iFmtID,
  2066.        ...)
  2067. {
  2068.   BOOL bResult = FALSE;
  2069.   va_list arglist;
  2070.   va_start(arglist, iFmtID);
  2071.   TCHAR szFormat[MAX_STRING_LENGTH];
  2072.   TCHAR szMsg[MAX_STRING_LENGTH];
  2073.  
  2074.   // Load the format string from the app's string resources.
  2075.   if (LoadString(m_hInstLog, iFmtID, szFormat, MAX_STRING_LENGTH))
  2076.   {
  2077.     // Use the format string and arguments to format the content text.
  2078.     wvsprintf(szMsg, szFormat, arglist);
  2079.  
  2080.     // Send the newly formated message string to the Log Listbox.
  2081.     bResult = Msg(szMsg);
  2082.   }
  2083.  
  2084.   return (bResult);
  2085. }
  2086.  
  2087.  
  2088. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  2089.   Method:   CMsgLog::Resize
  2090.  
  2091.   Summary:  Resizes the listbox to a new width and height.  Called during
  2092.             the parent window's WM_SIZE to fit the listbox to the client
  2093.             area of the parent window.  It only honors this request if it
  2094.             is an integral child window.
  2095.  
  2096.   Args:     int nWidth
  2097.               New width in pixels of the listbox.
  2098.             int nHeight
  2099.               New height in pixels of the listbox.
  2100.  
  2101.   Returns:  BOOL
  2102.               TRUE if successful; FALSE if not.
  2103. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  2104. BOOL CMsgLog::Resize(
  2105.        int nWidth,
  2106.        int nHeight)
  2107. {
  2108.   BOOL bResult = FALSE;
  2109.  
  2110.   if (m_bChild)
  2111.     bResult = ::MoveWindow(m_hWndLog, 0, 0, nWidth, nHeight, TRUE);
  2112.  
  2113.   return (bResult);
  2114. }
  2115.  
  2116.  
  2117. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  2118.   Method:   CMsgLog::Clear
  2119.  
  2120.   Summary:  Clears all logged messages from the message log listbox.
  2121.  
  2122.   Args:     void
  2123.  
  2124.   Returns:  BOOL
  2125.               TRUE.
  2126. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  2127. BOOL CMsgLog::Clear(
  2128.        void)
  2129. {
  2130.   if (IsWindow(m_hWndLog))
  2131.     ::SendMessage(
  2132.         m_hWndLog,
  2133.         LB_RESETCONTENT,
  2134.         0,
  2135.         0);
  2136.  
  2137.   return (TRUE);
  2138. }
  2139.  
  2140.  
  2141. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  2142.   Method:   CMsgLog::Copy
  2143.  
  2144.   Summary:  Copies the current content of the trace message log to the
  2145.             Windows clipboard.
  2146.  
  2147.   Args:     void
  2148.  
  2149.   Returns:  BOOL
  2150.               TRUE if success; FALSE if failure.
  2151. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  2152. BOOL CMsgLog::Copy(void)
  2153. {
  2154.   BOOL bResult = FALSE;
  2155.   DWORD iIndex;
  2156.   DWORD cbLen;
  2157.   HANDLE hClip;
  2158.   LPTSTR pLogData;
  2159.   TCHAR szLine[MAX_STRING_LENGTH];
  2160.   DWORD cItems = 0;
  2161.  
  2162.   if (IsWindow(m_hWndLog))
  2163.     cItems = ::SendMessage(m_hWndLog,LB_GETCOUNT,0,0);
  2164.  
  2165.   if (LB_ERR != cItems
  2166.       && 0 < cItems
  2167.       && MAX_LOG_LINES > cItems
  2168.       && OpenClipboard(m_hWndLog))
  2169.   {
  2170.     if (EmptyClipboard())
  2171.     {
  2172.       m_hLogData = GlobalAlloc(GHND, cItems * MAX_STRING_LENGTH);
  2173.       if (m_hLogData)
  2174.       {
  2175.         // Lock the global memory while we write to it.
  2176.         pLogData = (LPTSTR) GlobalLock(m_hLogData);
  2177.         // Loop thru the log text lines and concat them to one big string.
  2178.         for (iIndex = 0; iIndex < cItems; iIndex++)
  2179.         {
  2180.           // Get each log text line.
  2181.           cbLen = ::SendMessage(
  2182.                       m_hWndLog,
  2183.                       LB_GETTEXT,
  2184.                       (WPARAM)iIndex,
  2185.                       (LPARAM) (LPSTR)szLine);
  2186.           if (LB_ERR != cbLen && (MAX_STRING_LENGTH-3) > cbLen)
  2187.           {
  2188.             // Put CRLF at end.
  2189.             szLine[cbLen]   = '\r';
  2190.             szLine[cbLen+1] = '\n';
  2191.             szLine[cbLen+2] = '\0';
  2192.             if (0 == iIndex)
  2193.               lstrcpy((LPTSTR)pLogData, szLine);
  2194.             else
  2195.               lstrcat((LPTSTR)pLogData, szLine);
  2196.           }
  2197.         }
  2198.         // We're done with writing so unlock the memory block.
  2199.         GlobalUnlock(m_hLogData);
  2200.         // If there were any lines at all then copy them to clipboard.
  2201.         if (0 < iIndex)
  2202.           hClip = SetClipboardData(CF_TEXT, m_hLogData);
  2203.         if (!hClip)
  2204.         {
  2205.           // If we couldn't clip the data then free the global handle.
  2206.           GlobalFree(m_hLogData);
  2207.           m_hLogData = 0;
  2208.         }
  2209.         // We're done with the clipboard so close it.
  2210.         bResult = CloseClipboard();
  2211.       }
  2212.     }
  2213.   }
  2214.  
  2215.   return (bResult);
  2216. }
  2217.  
  2218.  
  2219. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  2220.   Method:   CSendLog::CreateServerLog
  2221.  
  2222.   Summary:  Creates the ListBox as a child window to fill the client area
  2223.             of the specified local server's parent window.
  2224.  
  2225.   Args:     HINSTANCE hInst,
  2226.               Instance handle of the application.
  2227.             HWND hWndparent,
  2228.               Window handle for the parent window of the listbox.
  2229.             BOOL bChild)
  2230.               Flag to create the listbox as a child window.  TRUE means fit
  2231.               the child window to fill the client area of the parent window.
  2232.               FALSE means the window is a separate (but owned) window.
  2233.  
  2234.   Returns:  BOOL
  2235.               TRUE if successful; FALSE if not.
  2236. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  2237. BOOL CSendLog::CreateServerLog(
  2238.        HINSTANCE hInst,
  2239.        HWND hWndParent,
  2240.        BOOL bChild)
  2241. {
  2242.   BOOL bResult = FALSE;
  2243.   HWND hWnd;
  2244.   RECT rect;
  2245.   DWORD dwStyle = WS_VISIBLE | WS_VSCROLL | WS_HSCROLL | LBS_NOSEL |
  2246.                     LBS_NOINTEGRALHEIGHT;
  2247.  
  2248.   if (bChild)
  2249.     dwStyle |= WS_CHILD;
  2250.   else
  2251.     dwStyle |= WS_OVERLAPPEDWINDOW;
  2252.  
  2253.   if (IsWindow(hWndParent))
  2254.   {
  2255.     GetClientRect(hWndParent, &rect);
  2256.  
  2257.     // Create the child ListBox window and fill the parent window with it.
  2258.     hWnd = ::CreateWindowEx(
  2259.                0,                // Extended Window Style
  2260.                TEXT("LISTBOX"),  // Class Name
  2261.                NULL,             // No Window Title
  2262.                dwStyle,          // The window style
  2263.                0,                // (x,y)=Upper left of Parent window
  2264.                0,
  2265.                rect.right,       // Width; Fill Client Window
  2266.                rect.bottom,      // Hight
  2267.                hWndParent,       // Parent Window Handle
  2268.                0,                // No menu
  2269.                hInst,            // App Instance Handle
  2270.                NULL);            // Window Creation Data
  2271.  
  2272.     if (NULL != hWnd)
  2273.     {
  2274.       // Remember the instance of this application.
  2275.       m_hInstSender = hInst;
  2276.       // Remember the window handle of this Sending application.
  2277.       m_hWndSender = hWndParent;
  2278.       // Remember the window handle of the new listbox window.
  2279.       m_hWndListBox = hWnd;
  2280.       // Remember if this is a child window (bChild==TRUE).
  2281.       m_bChild = bChild;
  2282.       // Return success.
  2283.       bResult = TRUE;
  2284.     }
  2285.   }
  2286.  
  2287.   return (bResult);
  2288. }
  2289.  
  2290.  
  2291. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  2292.   Method:   CSendLog::SetClient
  2293.  
  2294.   Summary:  Initialize the SendLog facility for logging to the client.
  2295.  
  2296.   Args:     HINSTANCE hInstSender,
  2297.               Instance handle of the sending server application.
  2298.             HWND hWndSender,
  2299.               Window handle for the sending server's main window.
  2300.             HWND hWndReceiver,
  2301.               Window handle for the destination receiving client window.
  2302.  
  2303.   Returns:  BOOL
  2304.               Returns TRUE.
  2305. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  2306. BOOL CSendLog::SetClient(
  2307.        HINSTANCE hInstSender,
  2308.        HWND hWndSender,
  2309.        HWND hWndReceiver)
  2310. {
  2311.   BOOL bOk;
  2312.  
  2313.   bOk = (NULL != hWndReceiver);
  2314.  
  2315.   if (bOk)
  2316.   {
  2317.     // Remember the instance of this sending application.
  2318.     m_hInstSender = hInstSender;
  2319.     // Remember the main window of this sending application.
  2320.     m_hWndSender = hWndSender;
  2321.     // Remember the window handle of the receiving window.
  2322.     m_hWndReceiver = hWndReceiver;
  2323.   }
  2324.  
  2325.   return bOk;
  2326. }
  2327.  
  2328.  
  2329. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  2330.   Method:   CSendLog::LogToServer
  2331.  
  2332.   Summary:  Switches logging to either server or client display.
  2333.  
  2334.   Args:     BOOL bLogToServer
  2335.               TRUE for logging to server; FALSE for logging to client.
  2336.  
  2337.   Returns:  BOOL
  2338.               New logging status.
  2339. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  2340. BOOL CSendLog::LogToServer(
  2341.        BOOL bLogToServer)
  2342. {
  2343.   BOOL bResult = bLogToServer;
  2344.  
  2345.   m_bLogToServer = bLogToServer;
  2346.  
  2347.   return (bResult);
  2348. }
  2349.  
  2350.  
  2351. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  2352.   Method:   CSendLog::Msg
  2353.  
  2354.   Summary:  Logs a message string as a separate line in the receiving
  2355.             CMsgLog facility.
  2356.  
  2357.   Args:     LPTSTR szMsg
  2358.               Pointer to String of the message to display/log.
  2359.  
  2360.   Returns:  BOOL bResult
  2361.               TRUE if successful; FALSE if not.
  2362. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  2363. BOOL CSendLog::Msg(
  2364.        LPTSTR szMsg)
  2365. {
  2366.   BOOL bResult = FALSE;
  2367.   COPYDATASTRUCT cds;
  2368.  
  2369.   if (NULL != szMsg)
  2370.   {
  2371.     if (m_bLogToServer)
  2372.     {
  2373.       int iIndex;
  2374.  
  2375.       bResult = TRUE;
  2376.       ::SendMessage(
  2377.           m_hWndListBox,
  2378.           LB_ADDSTRING,
  2379.           0,
  2380.           (LPARAM)szMsg);
  2381.       iIndex = ::SendMessage(
  2382.                    m_hWndListBox,
  2383.                    LB_GETCOUNT,
  2384.                    0,
  2385.                    0);
  2386.       if (LB_ERR != iIndex && iIndex > 0)
  2387.       {
  2388.         --iIndex;
  2389.         ::SendMessage(
  2390.             m_hWndListBox,
  2391.             LB_SETCURSEL,
  2392.             (WPARAM)iIndex,
  2393.             0);
  2394.       }
  2395.     }
  2396.     else
  2397.     {
  2398.       cds.dwData = 0;
  2399.       cds.cbData = sizeof(TCHAR) * (lstrlen(szMsg)+1);
  2400.       cds.lpData = szMsg;
  2401.  
  2402.       bResult = SendMessage(
  2403.                   m_hWndReceiver,
  2404.                   WM_COPYDATA,
  2405.                   (WPARAM) m_hWndSender,
  2406.                   (LPARAM) &cds);
  2407.     }
  2408.  
  2409.     #if defined(DEBUG)
  2410.       ::OutputDebugString(szMsg);
  2411.     #endif
  2412.   }
  2413.  
  2414.   return (bResult);
  2415. }
  2416.  
  2417.  
  2418. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  2419.   Method:   CSendLog::MsgFmt
  2420.  
  2421.   Summary:  Logs a printf-style formated message as a separate line in the
  2422.             receiving CMsgLog facility.
  2423.  
  2424.   Args:     LPTSTR szFmtMsg
  2425.               The format/message string to display/log.
  2426.             [...]
  2427.               Arguments to match those specified in the format string.
  2428.  
  2429.   Returns:  BOOL
  2430.               TRUE if successful; FALSE if not.
  2431. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  2432. BOOL CSendLog::MsgFmt(
  2433.        LPTSTR szFmtMsg,
  2434.        ...)
  2435. {
  2436.   BOOL bResult = FALSE;
  2437.   va_list arglist;
  2438.   va_start(arglist, szFmtMsg);
  2439.   TCHAR szMsg[MAX_STRING_LENGTH];
  2440.  
  2441.   if (NULL != szFmtMsg)
  2442.   {
  2443.     // Use the format string and arguments to format the content text.
  2444.     wvsprintf(szMsg, szFmtMsg, arglist);
  2445.     // Send the newly formated message string to the trace Log Listbox.
  2446.     bResult = Msg(szMsg);
  2447.   }
  2448.  
  2449.   return (bResult);
  2450. }
  2451.  
  2452.  
  2453. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  2454.   Method:   CSendLog::MsgID
  2455.  
  2456.   Summary:  Logs a message string as a separate line in the receiving
  2457.             CMsgLog facility.
  2458.  
  2459.   Args:     int iMsgID
  2460.               String Resource ID for the message to display/log.
  2461.  
  2462.   Returns:  BOOL
  2463.               TRUE if successful; FALSE if not.
  2464. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  2465. BOOL CSendLog::MsgID(
  2466.        int iMsgID)
  2467. {
  2468.   BOOL bResult = FALSE;
  2469.   TCHAR szMsg[MAX_STRING_LENGTH];
  2470.  
  2471.   // Load the message string from the sender's resources and log/send it.
  2472.   if (LoadString(m_hInstSender, iMsgID, szMsg, MAX_STRING_LENGTH))
  2473.     bResult = Msg(szMsg);
  2474.  
  2475.   return (bResult);
  2476. }
  2477.  
  2478.  
  2479. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  2480.   Method:   CSendLog::MsgFmtID
  2481.  
  2482.   Summary:  Logs a printf-style formated message as a separate line in the
  2483.             receiving CMsgLog facility.
  2484.  
  2485.   Args:     int iFmtID
  2486.               The resource ID for the format/message string to display/log.
  2487.             [...]
  2488.               Arguments to match those specified in the format string.
  2489.  
  2490.   Returns:  BOOL
  2491.               TRUE if successful; FALSE if not.
  2492. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  2493. BOOL CSendLog::MsgFmtID(
  2494.        int iFmtID,
  2495.        ...)
  2496. {
  2497.   BOOL bResult = FALSE;
  2498.   va_list arglist;
  2499.   va_start(arglist, iFmtID);
  2500.   TCHAR szFormat[MAX_STRING_LENGTH];
  2501.   TCHAR szMsg[MAX_STRING_LENGTH];
  2502.  
  2503.   // Load the format string from the app's string resources.
  2504.   if (LoadString(m_hInstSender, iFmtID, szFormat, MAX_STRING_LENGTH))
  2505.   {
  2506.     // Use the format string and arguments to format the content text.
  2507.     wvsprintf(szMsg, szFormat, arglist);
  2508.     // Send the newly formated message string to the trace Log Listbox.
  2509.     bResult = Msg(szMsg);
  2510.   }
  2511.  
  2512.   return (bResult);
  2513. }
  2514.  
  2515.  
  2516. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  2517.   Method:   CSendLog::Resize
  2518.  
  2519.   Summary:  Resizes the listbox to a new width and height.  Called during
  2520.             the parent window's WM_SIZE to fit the listbox to the client
  2521.             area of the parent window.  It only honors this request if it
  2522.             is an integral child window.
  2523.  
  2524.   Args:     int nWidth
  2525.               New width in pixels of the listbox.
  2526.             int nHeight
  2527.               New height in pixels of the listbox.
  2528.  
  2529.   Returns:  BOOL
  2530.               TRUE if successful; FALSE if not.
  2531. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  2532. BOOL CSendLog::Resize(
  2533.        int nWidth,
  2534.        int nHeight)
  2535. {
  2536.   BOOL bResult = FALSE;
  2537.  
  2538.   if (m_bChild)
  2539.     bResult = ::MoveWindow(m_hWndListBox, 0, 0, nWidth, nHeight, TRUE);
  2540.  
  2541.   return (bResult);
  2542. }
  2543.  
  2544.  
  2545. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  2546.   Method:   CSendLog::Clear
  2547.  
  2548.   Summary:  Clears all logged messages from the message log listbox.
  2549.  
  2550.   Args:     void
  2551.  
  2552.   Returns:  BOOL
  2553.               TRUE.
  2554. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  2555. BOOL CSendLog::Clear(
  2556.        void)
  2557. {
  2558.   ::SendMessage(
  2559.       m_hWndListBox,
  2560.       LB_RESETCONTENT,
  2561.       0,
  2562.       0);
  2563.  
  2564.   return (TRUE);
  2565. }
  2566.  
  2567.  
  2568. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  2569.   Method:   CSendLog::Copy
  2570.  
  2571.   Summary:  Copies the current content of the trace message log to the
  2572.             Windows clipboard.
  2573.  
  2574.   Args:     void
  2575.  
  2576.   Returns:  BOOL
  2577.               TRUE if success; FALSE if failure.
  2578. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  2579. BOOL CSendLog::Copy(void)
  2580. {
  2581.   BOOL bResult = FALSE;
  2582.   DWORD iIndex;
  2583.   DWORD cbLen;
  2584.   HANDLE hClip;
  2585.   LPTSTR pLogData;
  2586.   TCHAR szLine[MAX_STRING_LENGTH];
  2587.   DWORD cItems = ::SendMessage(m_hWndListBox,LB_GETCOUNT,0,0);
  2588.  
  2589.   if (LB_ERR != cItems
  2590.       && 0 < cItems
  2591.       && MAX_LOG_LINES > cItems
  2592.       && OpenClipboard(m_hWndListBox))
  2593.   {
  2594.     if (EmptyClipboard())
  2595.     {
  2596.       m_hLogData = GlobalAlloc(GHND, cItems * MAX_STRING_LENGTH);
  2597.       if (m_hLogData)
  2598.       {
  2599.         // Lock the global memory while we write to it.
  2600.         pLogData = (LPTSTR) GlobalLock(m_hLogData);
  2601.         // Loop thru the log text lines and concat them to one big string.
  2602.         for (iIndex = 0; iIndex < cItems; iIndex++)
  2603.         {
  2604.           // Get each log text line.
  2605.           cbLen = ::SendMessage(
  2606.                       m_hWndListBox,
  2607.                       LB_GETTEXT,
  2608.                       (WPARAM)iIndex,
  2609.                       (LPARAM) (LPSTR)szLine);
  2610.           if (LB_ERR != cbLen && (MAX_STRING_LENGTH-3) > cbLen)
  2611.           {
  2612.             // Put CRLF at end.
  2613.             szLine[cbLen]   = '\r';
  2614.             szLine[cbLen+1] = '\n';
  2615.             szLine[cbLen+2] = '\0';
  2616.             if (0 == iIndex)
  2617.               lstrcpy((LPTSTR)pLogData, szLine);
  2618.             else
  2619.               lstrcat((LPTSTR)pLogData, szLine);
  2620.           }
  2621.         }
  2622.         // We're done with writing so unlock the memory block.
  2623.         GlobalUnlock(m_hLogData);
  2624.         // If there were any lines at all then copy them to clipboard.
  2625.         if (0 < iIndex)
  2626.           hClip = SetClipboardData(CF_TEXT, m_hLogData);
  2627.         if (!hClip)
  2628.         {
  2629.           // If we couldn't clip the data then free the global handle.
  2630.           GlobalFree(m_hLogData);
  2631.           m_hLogData = 0;
  2632.         }
  2633.         // We're done with the clipboard so close it.
  2634.         bResult = CloseClipboard();
  2635.       }
  2636.     }
  2637.   }
  2638.  
  2639.   return (bResult);
  2640. }
  2641.