home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / mfc / ole / oleview / util.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-03-27  |  43.1 KB  |  1,466 lines

  1. // util.cpp
  2.  
  3. // This is a part of the Microsoft Foundation Classes C++ library.
  4. // Copyright (C) 1992-1998 Microsoft Corporation
  5. // All rights reserved.
  6. //
  7. // This source code is only intended as a supplement to the
  8. // Microsoft Foundation Classes Reference and related
  9. // electronic documentation provided with the library.
  10. // See these sources for detailed information regarding the
  11. // Microsoft Foundation Classes product.
  12.  
  13. #include "stdafx.h"
  14. #include "util.h"
  15. #include "shadow.h"
  16. /////
  17. // Utility functions
  18. //
  19. static TCHAR szBold[]       = _T("Bold") ;
  20. static TCHAR szItalic[]      = _T("Italic") ;
  21. static TCHAR szBoldItalic[]  = _T("Bold Italic") ;
  22. static TCHAR szRegular[]     = _T("Regular") ;
  23.  
  24. void ErrorMessage( const CString& str, HRESULT hr )
  25. {
  26.     LPVOID lpMessageBuffer = NULL ;
  27.     if (FormatMessage(
  28.           FORMAT_MESSAGE_ALLOCATE_BUFFER |
  29.           FORMAT_MESSAGE_FROM_SYSTEM,
  30.           NULL,
  31.           GetScode(hr),
  32.           MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
  33.           (LPTSTR) &lpMessageBuffer,
  34.           0,
  35.           NULL ))
  36.     {
  37.         CString str2 ;
  38.         str2.Format( _T("\n%s %s"), (LPCTSTR)lpMessageBuffer, (LPCTSTR)HRtoString(hr) ) ;
  39.         str2 = str + str2 ;
  40.         LocalFree(lpMessageBuffer) ;
  41.         AfxMessageBox( str2 ) ;
  42.     }
  43.     else
  44.     {
  45.         CString str2 ;
  46.         str2.Format( _T("\n<No system message defined> %s"), (LPCTSTR)HRtoString(hr) ) ;
  47.         str2 = str + str2 ;
  48.         AfxMessageBox( str2 ) ;
  49.     }
  50. }
  51.  
  52.  
  53. extern "C"
  54. int EXPORT CALLBACK
  55.     fnEnumReallyEx( LPENUMLOGFONT lpLogFont,
  56.                 LPNEWTEXTMETRIC lpTextMetrics,
  57.                 int nFontType,
  58.                 LPENUMLOGFONT lpELF ) ;
  59.  
  60.  /****************************************************************
  61.  *  HFONT WINAPI
  62.  *    ReallyCreateFontEx( HDC hDC, LPTSTR lpszFace,
  63.  *                        LPTSTR lpszStyle, int nPointSize, UINT uiFlags )
  64.  *
  65.  *  Description:
  66.  *
  67.  *    Creates a font based on facename, pointsize, and style.
  68.  *
  69.  *    Uses 3.1 API's to correctly select TT fonts.
  70.  *
  71.  *    HDC   hDC         the target DC (optional)
  72.  *
  73.  *    LPTSTR lpszFace    pointer the facename (required)
  74.  *
  75.  *    LPTSTR lpszStyle   pointer to the style (like "Demibold Italic")
  76.  *                      (optional).
  77.  *
  78.  *    int nPointSize  size in points (required)
  79.  *
  80.  *    UINT  uiFlags      Flags, same as for ReallyCreateFont() except
  81.  *                      RCF_BOLD and RCF_ITALIC are ignored if lpszStyle
  82.  *                      is not NULL.
  83.  *
  84.  *  Comments:
  85.  *
  86.  ****************************************************************/
  87.  
  88.  
  89. CFont* ReallyCreateFont( HDC hDC, LPTSTR lpszFace, LPTSTR lpszStyle, UINT nPointSize, UINT uiFlags )
  90. {
  91.    ENUMLOGFONT  elf ;
  92.    FONTENUMPROC lpfn ;
  93.    HDC          hdcCur ;
  94.  
  95.    if (!(lpfn = (FONTENUMPROC)MakeProcInstance( (FARPROC)fnEnumReallyEx, AfxGetInstanceHandle() )))
  96.       return NULL ;
  97.  
  98.    // if the DC wasn't specified then use the display...
  99.    //
  100.    if (!hDC)
  101.    {
  102.       if (!(hdcCur = GetDC( NULL )))
  103.       {
  104.          FreeProcInstance( (FARPROC)lpfn ) ;
  105.          return FALSE ;
  106.       }
  107.    }
  108.    else
  109.       hdcCur = hDC ;
  110.  
  111.    if (hdcCur)
  112.    {
  113.       /*
  114.        * EnumFontFamilies takes the family name as the second param.
  115.        * For example the family name might be "Lucida Sans" and the
  116.        * style might be "Demibold Roman".
  117.        *
  118.        * The last parameter is app. defined.  In our case we pass a
  119.        * structure that has a LOGFONT and a char array as it's members.
  120.        * We put the style in the char array, and when this function
  121.        * returns the LOGFONT has the logfont for the font we want to create.
  122.        */
  123.       if (lpszStyle)
  124.          lstrcpy( (LPTSTR)elf.elfStyle, lpszStyle ) ;
  125.       else
  126.         switch( uiFlags & ~(RCF_NODEFAULT | RCF_STRIKEOUT | RCF_UNDERLINE) )
  127.         {
  128.             case RCF_BOLD:
  129.                 lstrcpy( (LPTSTR)elf.elfStyle, szBold ) ;
  130.             break ;
  131.  
  132.             case RCF_ITALIC:
  133.                 lstrcpy( (LPTSTR)elf.elfStyle, szItalic ) ;
  134.             break ;
  135.  
  136.             case RCF_BOLD | RCF_ITALIC:
  137.                 lstrcpy( (LPTSTR)elf.elfStyle, szBold ) ;
  138.                 lstrcat( (LPTSTR)elf.elfStyle, _T(" ") ) ;
  139.                 lstrcat( (LPTSTR)elf.elfStyle, szItalic ) ;
  140.             break ;
  141.  
  142.             default:
  143.                 lstrcpy( (LPTSTR)elf.elfStyle, szRegular ) ;
  144.                 lpszStyle = (LPTSTR)elf.elfStyle ;
  145.             break ;
  146.         }
  147.  
  148.       if (lpszFace)
  149.          EnumFontFamilies( hdcCur, lpszFace, lpfn, (LPARAM)(LPVOID)&elf ) ;
  150.  
  151.       if (!lpszFace || lstrcmpi( elf.elfLogFont.lfFaceName, lpszFace))
  152.       {
  153.          if ((uiFlags & RCF_NODEFAULT) == RCF_NODEFAULT)
  154.          {
  155.             FreeProcInstance( (FARPROC)lpfn ) ;
  156.  
  157.             if (hdcCur != hDC)
  158.                ReleaseDC( NULL, hdcCur ) ;
  159.  
  160.             return NULL ;
  161.  
  162.          }
  163.          else
  164.             GetObject( GetStockObject( SYSTEM_FONT ),
  165.                        sizeof( LOGFONT ), (LPTSTR)&elf.elfLogFont ) ;
  166.       }
  167.  
  168.       //
  169.       // See pages 4-48, 4-49, of Reference Vol. 1 for explaination
  170.       // of why we set lfWidth to 0
  171.       //
  172.       elf.elfLogFont.lfWidth = 0 ;
  173.  
  174.       //
  175.       // The equation for calculating point size based on font
  176.       // height (a point is 1/72 of an inch) is:
  177.       //
  178.       //
  179.       //    pt = (height * 72) / number of pixels in the Y direction
  180.       //
  181.       // Using GetTextMetrics() you should calculate height as:
  182.       //
  183.       //    height = tm.tmHeight - tm.tmInternalLeading
  184.       //
  185.       // This is because Windows' interpretation of a font height
  186.       // is different from everyone else's.
  187.       //
  188.       //
  189.       elf.elfLogFont.lfHeight = -MulDiv( nPointSize,
  190.                              GetDeviceCaps( hdcCur, LOGPIXELSY),
  191.                              72 )  ;
  192.  
  193.       elf.elfLogFont.lfStrikeOut = (BYTE)((uiFlags & RCF_STRIKEOUT) == RCF_STRIKEOUT) ;
  194.       elf.elfLogFont.lfUnderline = (BYTE)((uiFlags & RCF_UNDERLINE) == RCF_UNDERLINE) ;
  195.  
  196.       if (!lpszStyle)
  197.       {
  198.          elf.elfLogFont.lfWeight    = (uiFlags & RCF_BOLD) ? FW_BOLD : FW_NORMAL ;
  199.          elf.elfLogFont.lfItalic    = (BYTE)((uiFlags & RCF_ITALIC) == RCF_ITALIC) ;
  200.       }
  201.  
  202.    }
  203.    FreeProcInstance( (FARPROC)lpfn ) ;
  204.  
  205.    if (hdcCur != hDC)
  206.       ReleaseDC( NULL, hdcCur ) ;
  207.  
  208.    CFont * pFont = new CFont ;
  209.    pFont->CreateFontIndirect( &elf.elfLogFont ) ;
  210.  
  211.    return pFont ;
  212.  
  213. } /* ReallyCreateFontEx()  */
  214.  
  215. /****************************************************************
  216.  *  int CALLBACK
  217.  *    fnEnumReallyEx( LPLOGFONT lpLogFont,
  218.  *                  LPTEXTMETRIC lpTextMetrics,
  219.  *                  int nFontType, LPLOGFONT lpLF )
  220.  *
  221.  *  Description:
  222.  *
  223.  *    Call back for EnumFonts and ReallySelectFontEx().
  224.  *
  225.  *    DO NOT FORGET TO EXPORT!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  226.  *
  227.  *  Comments:
  228.  *
  229.  ****************************************************************/
  230. extern "C"
  231. int EXPORT CALLBACK
  232. fnEnumReallyEx( LPENUMLOGFONT lpELFin,
  233.                 LPNEWTEXTMETRIC /*lpTextMetrics*/,
  234.                 int nFontType,
  235.                 LPENUMLOGFONT lpELF )
  236. {
  237.    /* If we are not true type, then we will only be called once,
  238.     * in in this case we need to compare the weight and italic
  239.     * members of the TM struct to some strings.
  240.     */
  241.    if (!(nFontType & TRUETYPE_FONTTYPE))
  242.    {
  243.       lpELF->elfLogFont = lpELFin->elfLogFont ;
  244.  
  245.        // HACK TO FIX WindowsJ bug
  246.        //
  247.        LOGFONT lf ;
  248.        GetObject( GetStockObject( SYSTEM_FONT ),
  249.                            sizeof( LOGFONT ), (LPTSTR)&lf ) ;
  250.        lpELF->elfLogFont.lfCharSet = lf.lfCharSet;
  251.  
  252.       /* Parse szStyle.  It may be something like "Bold Italic" or
  253.        * "Demibold Italic".  Let's be somewhat smart about this.
  254.        *
  255.        * If "Demibold Italic" is passed in then he wants bold and
  256.        * italic.
  257.        *
  258.        * We want to search szStyle for the word "bold".  Got a better
  259.        * search strategy, go ahead and put it here (but tell me about it!).
  260.        *
  261.        * WARNING!!!
  262.        *   The style string is language dependent.
  263.        */
  264.       if (lpELF->elfStyle)
  265.       {
  266.          /*
  267.           * Assume "Regular"
  268.           */
  269.          lpELF->elfLogFont.lfWeight = FW_NORMAL ;
  270.          lpELF->elfLogFont.lfItalic = FALSE ;
  271.  
  272.          if (!lstrcmpi( (LPTSTR)lpELF->elfStyle, szBold ))
  273.          {
  274.             lpELF->elfLogFont.lfWeight = FW_BOLD ;
  275.             return 0 ;
  276.          }
  277.  
  278.          if (!lstrcmpi( (LPTSTR)lpELF->elfStyle, szItalic))
  279.          {
  280.             lpELF->elfLogFont.lfItalic = TRUE ;
  281.             return 0 ;
  282.          }
  283.  
  284.          if (!lstrcmpi( (LPTSTR)lpELF->elfStyle, szBoldItalic ))
  285.          {
  286.             lpELF->elfLogFont.lfWeight = FW_BOLD ;
  287.             lpELF->elfLogFont.lfItalic = TRUE ;
  288.             return 0 ;
  289.          }
  290.       }
  291.  
  292.       return 0 ;  /* stop the enumeration */
  293.  
  294.    }
  295.  
  296.    /* We now know we have a TT font.  For each style in the
  297.     * family passed in, we will get here.
  298.     *
  299.     * If we get an exact match copy it into lpELF and return.
  300.     * Otherwise get as close as possible.
  301.     */
  302.    if (0==lstrcmpi( (LPTSTR)lpELF->elfStyle, (LPTSTR)lpELFin->elfStyle ))
  303.    {
  304.       *lpELF = *lpELFin;
  305.       return 0;
  306.    }
  307.  
  308.    lpELF->elfLogFont = lpELFin->elfLogFont ;
  309.  
  310.    return 1 ;
  311.  
  312. }/* fnEnumReallyEx() */
  313.  
  314.  
  315. /*
  316.  * PointerToNthField
  317.  *
  318.  * Purpose:
  319.  *  Returns a pointer to the beginning of the nth field.
  320.  *  Assumes null-terminated string.
  321.  *
  322.  * Parameters:
  323.  *  lpString        string to parse
  324.  *  nField            field to return starting index of.
  325.  *  chDelimiter       char that delimits fields
  326.  *
  327.  * Return Value:
  328.  *  LPTSTR             pointer to beginning of nField field.
  329.  *                    NOTE: If the null terminator is found
  330.  *                          Before we find the Nth field, then
  331.  *                          we return a pointer to the null terminator -
  332.  *                          calling app should be sure to check for
  333.  *                          this case.
  334.  *
  335.  */
  336. LPTSTR FAR PASCAL PointerToNthField(LPTSTR lpszString, int nField, TCHAR chDelimiter)
  337. {
  338.    LPTSTR lpField = lpszString;
  339.    int   cFieldFound = 1;
  340.  
  341.    if (1 ==nField)
  342.       return lpszString;
  343.  
  344.    while (*lpField != '\0')
  345.    {
  346.  
  347.       if (*lpField++ == chDelimiter)
  348.       {
  349.  
  350.          cFieldFound++;
  351.  
  352.          if (nField == cFieldFound)
  353.             return lpField;
  354.       }
  355.    }
  356.  
  357.    return lpField;
  358.  
  359. }
  360.  
  361. #include <ctype.h>
  362. // Very useful API that parses a LONG out of a string, updating
  363. // the string pointer before it returns.
  364. //
  365. LONG WINAPI ParseOffNumber( LPTSTR FAR *lplp, LPINT lpConv )
  366. {
  367.     LPTSTR lp = *lplp;
  368.     LONG  lValue=0;
  369.     int   sign=1;
  370.  
  371.     while( isspace(*lp) )
  372.         lp++;
  373.     if( *lp=='-' )
  374.     {
  375.         sign = -1;
  376.         lp++;
  377.     }
  378.     if (*lp=='\0')
  379.     {
  380.         *lpConv = FALSE;
  381.         return 0L;
  382.     }
  383.  
  384.     // Is it a decimal number
  385.     #ifdef UNICODE
  386.     if( _wcsnicmp( lp, (LPTSTR)_T("0x"),2) )
  387.     #else
  388.     #ifdef WIN32
  389.     if( strnicmp( lp, (LPTSTR)_T("0x"),2) )
  390.     #else
  391.     if( _fstrnicmp( lp, (LPTSTR)_T("0x"),2) )
  392.     #endif
  393.     #endif
  394.     {
  395.  
  396.         while( isdigit(*lp) )
  397.         {
  398.             lValue *= 10;
  399.             lValue += (*lp - (TCHAR)'0');
  400.             lp++;
  401.         }
  402.     }
  403.     else
  404.     {
  405.         lp+=2;
  406.  
  407.         while( isxdigit(*lp) )
  408.         {
  409.             lValue *= 16;
  410.             if( isdigit(*lp) )
  411.                 lValue += (*lp - (TCHAR)'0');
  412.             else
  413.                 lValue += (toupper(*lp) - (TCHAR)'A' + 10);
  414.             lp++;
  415.         }
  416.     }
  417.     while( isspace(*lp) )
  418.         lp++;
  419.  
  420.     lValue *= (long)sign;
  421.  
  422.     if (*lp==(TCHAR)',')
  423.     {
  424.         lp++;
  425.         while( isspace(*lp) )
  426.             lp++;
  427.         *lplp = lp;
  428.         if (lpConv)
  429.             *lpConv = (int)TRUE;
  430.     }
  431.     else
  432.     {
  433.         *lplp = lp;
  434.         if (lpConv)
  435.             *lpConv = (int)(*lp=='\0');
  436.     }
  437.     return lValue;
  438.  
  439. } //*** ParseOffNumber
  440.  
  441.  
  442. /*************************************************************************
  443.  *  void WINAPI
  444.  *  DlgCenter( HWND hwndCenter, HWND hwndWithin, BOOL fClient )
  445.  *
  446.  *  Description:
  447.  *
  448.  *    Centers a window within another window.
  449.  *
  450.  *  Type/Parameter
  451.  *          Description
  452.  *
  453.  *    HWND  hwndCenter
  454.  *          Window to center.  This does not have to be a child of
  455.  *          hwndWithin.
  456.  *
  457.  *    HWND  hwndWithin
  458.  *          Window to center the above window within.  Can be NULL.
  459.  *
  460.  *    BOOL  fClient
  461.  *          If TRUE the window is centered within the available client
  462.  *          area.  Otherwise it's centered within the entire window area.
  463.  *
  464.  *  Comments:
  465.  *
  466.  *************************************************************************/
  467. void WINAPI
  468. DlgCenter( HWND hwndCenter, HWND hwndWithin, BOOL fClient )
  469. {
  470.    RECT rcWithin ;
  471.    RECT rcCenter ;
  472.    int   x, y ;
  473.    int   dxCenter, dyCenter ;
  474.    int   dxScreen, dyScreen ;
  475.  
  476.    dxScreen = GetSystemMetrics( SM_CXSCREEN ) ;
  477.    dyScreen = GetSystemMetrics( SM_CYSCREEN ) ;
  478.  
  479.    if (!IsWindow(hwndCenter))
  480.       return ;
  481.  
  482.    if (hwndWithin && !IsWindow(hwndWithin))
  483.       return ;
  484.  
  485.    if (hwndWithin == NULL)
  486.    {
  487.       rcWithin.left = rcWithin.top = 0 ;
  488.       rcWithin.right = dxScreen ;
  489.       rcWithin.bottom = dyScreen ;
  490.    }
  491.    else
  492.    {
  493.       if (fClient)
  494.       {
  495.          /*
  496.           * First get screen coords of rectangle we're going to be
  497.           * centered within.
  498.           */
  499.          GetClientRect( hwndWithin, (LPRECT)&rcWithin ) ;
  500.          ClientToScreen( hwndWithin, (LPPOINT)&rcWithin.left ) ;
  501.          ClientToScreen( hwndWithin, (LPPOINT)&rcWithin.right ) ;
  502.       }
  503.       else
  504.          GetWindowRect( hwndWithin, (LPRECT)&rcWithin ) ;
  505.    }
  506.  
  507.    GetWindowRect( hwndCenter, (LPRECT)&rcCenter ) ;
  508.    dxCenter = rcCenter.right - rcCenter.left ;
  509.    dyCenter = rcCenter.bottom - rcCenter.top ;
  510.  
  511.    /*
  512.     * Now we have both the within and center rects in screen coords.
  513.     *
  514.     * SetWindowPos behaves differently for Non child windows
  515.     * than for child windows.  For popups it's coordinates
  516.     * are relative to the upper left corner of the screen.  For
  517.     * children it's coords are relative to the upper left corner
  518.     * of the client area of the parent.
  519.     */
  520.    x = ((rcWithin.right - rcWithin.left) - dxCenter) / 2 ;
  521.    y = ((rcWithin.bottom - rcWithin.top) - dyCenter) / 2 ;
  522.  
  523.    if (hwndWithin == GetParent( hwndCenter ) &&
  524.        !(GetWindowLong( hwndCenter, GWL_STYLE ) & WS_CHILD ))
  525.    {
  526.       x += rcWithin.left ;
  527.       y += rcWithin.top ;
  528.  
  529.       if (x + dxCenter > dxScreen )
  530.          x = dxScreen - dxCenter ;
  531.  
  532.       if (y + dyCenter > dyScreen )
  533.          y = dyScreen - dyCenter ;
  534.  
  535.       SetWindowPos( hwndCenter, NULL,
  536.                     max(x,0),
  537.                     max(y,0),
  538.                     0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER ) ;
  539.  
  540.       return ;
  541.    }
  542.  
  543.    SetWindowPos( hwndCenter, NULL,
  544.                  max(x,0),
  545.                  max(y,0),
  546.                  0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER ) ;
  547.  
  548.    return ;
  549.  
  550. }/* DlgCenter() */
  551.  
  552. /****************************************************************
  553.  *  VOID FAR PASCAL bmColorTranslateDC( HDC pbm, LPBITMAP lpBM,
  554.  *                                    COLORREF rgbOld, COLORREF rgbNew,
  555.  *                                    WORD  wStyle )
  556.  *
  557.  *  Description:
  558.  *
  559.  *    This function makes all pixels in the given DC that have the
  560.  *    color rgbOld have the color rgbNew.
  561.  *
  562.  *    wSytle can be BM_NORMAL or BM_DISABLED.  If it is BM_DISABLED
  563.  *    the bitmap will be translated so that it looks disabled.
  564.  *
  565.  *  Comments:
  566.  *
  567.  ****************************************************************/
  568. void bmColorTranslateDC( CDC* pdcMem, BITMAP *pBM, COLORREF rgbOld,
  569.                        COLORREF rgbNew )
  570. {
  571.    CDC      dcMask ;
  572.    CBitmap  bmMask ;
  573.    CBitmap* pbmMask ;
  574.    CBrush   brT ;
  575.    CBrush*  pbrT ;
  576.  
  577.    if (bmMask.CreateBitmap( pBM->bmWidth, pBM->bmHeight, 1, 1, NULL ))
  578.    {
  579.       if (dcMask.CreateCompatibleDC( pdcMem ))
  580.       {
  581.          // Select th mask bitmap into the mono DC
  582.          //
  583.          pbmMask = dcMask.SelectObject( &bmMask ) ;
  584.  
  585.          // Create the brush and select it into the bits DC
  586.          //
  587.          brT.CreateSolidBrush( rgbNew ) ;
  588.          pbrT = pdcMem->SelectObject( &brT ) ;
  589.  
  590.          // Do a color to mono bitblt to build the mask.  Generate
  591.          // 1's where the source is equal to the background.
  592.          //
  593.          pdcMem->SetBkColor( rgbOld ) ;
  594.          dcMask.BitBlt( 0, 0, pBM->bmWidth, pBM->bmHeight, pdcMem, 0, 0, SRCCOPY ) ;
  595.  
  596.          // Where the mask is 1, lay down the brush, where it is 0, leave
  597.          // the destination.
  598.          //
  599.          pdcMem->SetBkColor( RGBWHITE ) ;
  600.          pdcMem->SetTextColor( RGBBLACK ) ;
  601.  
  602. #define DSPDxax   0x00E20746L
  603.          pdcMem->BitBlt( 0, 0, pBM->bmWidth, pBM->bmHeight, &dcMask, 0, 0, DSPDxax ) ;
  604.  
  605.          pbmMask = dcMask.SelectObject( &bmMask ) ;
  606.  
  607.          pbrT = pdcMem->SelectObject( pbrT ) ;
  608.          brT.DeleteObject( ) ;
  609.  
  610.          dcMask.DeleteDC( ) ;
  611.       }
  612.       else
  613.       {
  614.          return ;
  615.       }
  616.  
  617.       bmMask.DeleteObject( ) ;
  618.    }
  619.  
  620. } /* bmColorTranslateDC()  */
  621.  
  622.  
  623. void bmColorTranslate( CBitmap* pbmSrc, COLORREF rgbNew )
  624. {
  625.    HDC      hdc ;
  626.    CDC      dc ;
  627.    CDC      dcMem ;
  628.    BITMAP   bmBits ;
  629.  
  630.    hdc = GetDC( NULL );
  631.    if (hdc)
  632.        dc.Attach( hdc ) ;
  633.    else
  634.        return ;
  635.  
  636.   if (dcMem.CreateCompatibleDC( &dc ))
  637.   {
  638.      // Get the bitmap struct needed by bmColorTranslateDC()
  639.      //
  640.      pbmSrc->GetObject( sizeof( BITMAP ), &bmBits ) ;
  641.  
  642.      // Select our bitmap into the memory DC
  643.      //
  644.      pbmSrc = dcMem.SelectObject( pbmSrc ) ;
  645.  
  646.      // Translate the sucker
  647.      //
  648.      bmColorTranslateDC( &dcMem, &bmBits, dcMem.GetPixel( 0, 0 ), rgbNew ) ;
  649.  
  650.      // Unselect our bitmap before deleting the DC
  651.      //
  652.      pbmSrc = dcMem.SelectObject( pbmSrc ) ;
  653.  
  654.      dcMem.DeleteDC( ) ;
  655.   }
  656.  
  657.   dc.Detach() ;
  658.  
  659.   ReleaseDC( NULL, hdc ) ;
  660.  
  661. } /* bmColorTranslate()  */
  662.  
  663.  
  664. CBitmap* bmLoadAndTranslate( UINT id, COLORREF rgb )
  665. {
  666.     CBitmap* pBitmap = new CBitmap ;
  667.  
  668.     if (pBitmap)
  669.     {
  670.         pBitmap->LoadBitmap( id ) ;
  671.         bmColorTranslate( pBitmap, rgb ) ;
  672.     }
  673.     return pBitmap ;
  674. }
  675.  
  676.  
  677. void DrawListBoxBitmap( CDC* pdc, CBitmap *pbmt, int x,  int y )
  678. {
  679.     CDC   dcMem ;
  680.     if (dcMem.CreateCompatibleDC( pdc ))
  681.     {
  682.         if (pbmt)
  683.         {
  684.             pbmt = dcMem.SelectObject( pbmt ) ;
  685.             pdc->BitBlt( x, y, 16, 16, &dcMem, 0, 0, SRCCOPY ) ;
  686.             dcMem.SelectObject( pbmt ) ;
  687.         }
  688.         dcMem.DeleteDC() ;
  689.     }
  690. }
  691.  
  692. // column text out
  693.  
  694. /****************************************************************
  695.  *  int WINAPI
  696.  *    GetTextMetricsCorrectly( HDC hDC, LPTEXTMETRIC lpTextMetrics )
  697.  *
  698.  *  Description:
  699.  *
  700.  *    This function gets the textmetrics of the font currently
  701.  *    selected into the hDC.  It returns the average char width as
  702.  *    the return value.
  703.  *
  704.  *    This function computes the average character width correctly
  705.  *    by using GetTextExtent() on the string "abc...xzyABC...XYZ"
  706.  *    which works out much better for proportional fonts.
  707.  *
  708.  *    Note that this function returns the same TEXTMETRIC
  709.  *    values that GetTextMetrics() does, it simply has a different
  710.  *    return value.
  711.  *
  712.  *  Comments:
  713.  *
  714.  ****************************************************************/
  715. int WINAPI
  716.    GetTextMetricsCorrectly( HDC hDC, LPTEXTMETRIC lpTM )
  717. {
  718.    int    nAveWidth ;
  719.    TCHAR  rgchAlphabet[52] ;
  720.    int  i ;
  721.    SIZE size ;
  722.  
  723.    // Get the TM of the current font.  Note that GetTextMetrics
  724.    // gets the incorrect AveCharWidth value for proportional fonts.
  725.    // This is the whole poshort in this exercise.
  726.    //
  727.    if (lpTM)
  728.     GetTextMetrics(hDC, lpTM);
  729.  
  730.    // If it's not a variable pitch font GetTextMetrics was correct
  731.    // so just return.
  732.    //
  733.    if (lpTM && !(lpTM->tmPitchAndFamily & FIXED_PITCH))
  734.       return lpTM->tmAveCharWidth ;
  735.    else
  736.    {
  737.       for ( i = 0 ; i <= 25 ; i++)
  738.          rgchAlphabet[i] = (TCHAR)(i+(int)'a') ;
  739.  
  740.       for ( i = 0 ; i<=25 ; i++)
  741.          rgchAlphabet[i+25] = (TCHAR)(i+(int)'A') ;
  742.  
  743.       GetTextExtentPoint( hDC, (LPTSTR)rgchAlphabet, 52, &size ) ;
  744.       nAveWidth = size.cx / 26 ;
  745.       nAveWidth = (nAveWidth + 1) / 2 ;
  746.    }
  747.  
  748.    // Return the calculated average char width
  749.    //
  750.    return nAveWidth ;
  751.  
  752. } /* GetTextMetricsCorrectly()  */
  753.  
  754. /****************************************************************
  755.  *
  756.  *  Description:
  757.  *
  758.  *    The ColumntTextOut function writes a character string at
  759.  *    the specified location, using tabs to identify column breaks.  Each
  760.  *    column is aligned according to the array of COLUMNSTRUCTs.
  761.  *
  762.  *    A COLUMNSTRUCT looks like this:
  763.  *
  764.  *    {
  765.  *       int   nLeft ;       // starting x position of the column
  766.  *       int   nRight ;      // ending x position of the column
  767.  *       UINT  uiFlags ;      // format flags
  768.  *    }
  769.  *
  770.  *    If a column has another column to the left of it, it's nLeft member
  771.  *    is ignored.
  772.  *
  773.  *    uiFlags can be any of the following:
  774.  *
  775.  *          #define DT_LEFT      0x0000
  776.  *          #define DT_CENTER    0x0001
  777.  *          #define DT_RIGHT     0x0002
  778.  *
  779.  *  Comments:
  780.  *
  781.  ****************************************************************/
  782. void WINAPI ColumnTextOut( HDC hdc, int nX, int nY, LPTSTR lpszIN,
  783.                             int cColumns, LPCOLUMNSTRUCT rgColumns )
  784. {
  785.  
  786. #define ETOFLAGS ETO_CLIPPED
  787.  
  788.     COLUMNSTRUCT   CS ;              // local copy for speed
  789.     RECT           rc ;              // current cell rect
  790.     int            cIteration = 0 ;  // what column
  791.     LPTSTR          lpNext ;          // next string (current is lpsz)
  792.     int            cch ;             // count of chars in current string
  793.     SIZE           size ;            // extent of current string
  794.     int            dx ;              // column width
  795.  
  796.     rc.left = nX ;
  797.     rc.top = nY ;
  798.     rc.right = 0 ;
  799.  
  800.     if (rgColumns == NULL || cColumns <= 1)
  801.     {
  802.         int Tab = 15 ;
  803.         TabbedTextOut( hdc, nX, nY, lpszIN, lstrlen(lpszIN), 1, &Tab, nX ) ;
  804.         return ;
  805.     }
  806.  
  807.     // For each sub string (bracketed by a tab)
  808.     //
  809.     LPTSTR lpsz = lpszIN ;
  810.     while (*lpsz)
  811.     {
  812.         if (cIteration >= cColumns)
  813.             return ;
  814.  
  815.         for (cch = 0, lpNext = lpsz ;
  816.              *lpNext != '\t' && *lpNext ;
  817.              lpNext++, cch++)
  818.             ;
  819.  
  820.         CS = rgColumns[cIteration] ;
  821.  
  822.         // If it's the leftmost column use
  823.         //
  824.         if (cIteration == 0)
  825.         {
  826.             rc.left = nX + CS.nLeft ;
  827.             rc.right = nX + CS.nRight ;
  828.         }
  829.         else
  830.             rc.right = nX + CS.nRight ;
  831.  
  832.         GetTextExtentPoint( hdc, lpsz, cch, &size ) ;
  833.         rc.bottom = rc.top + size.cy ;
  834.  
  835.         // If the width of the column is 0 do nothing
  836.         //
  837.         if ((dx = (rc.right - rc.left)) > 0)
  838.         {
  839.             switch(CS.uiFlags)
  840.             {
  841.                 case DT_CENTER:
  842.                     ExtTextOut( hdc, rc.left + ((dx - size.cx) / (int)2),
  843.                     rc.top, ETOFLAGS, &rc, lpsz, cch, NULL ) ;
  844.                 break ;
  845.  
  846.                 case DT_RIGHT:
  847.                     // If it's right justified then make the left border 0
  848.                     //
  849.                     //rc.left = nX + rgColumns[0].nLeft ;
  850.                     ExtTextOut( hdc, rc.left + (dx - size.cx),
  851.                     rc.top, ETOFLAGS, &rc, lpsz, cch, NULL ) ;
  852.                 break ;
  853.  
  854.                 case DT_LEFT:
  855.                 default:
  856.                     ExtTextOut( hdc, rc.left, rc.top, ETOFLAGS, &rc, lpsz, cch, NULL ) ;
  857.                 break ;
  858.             }
  859.         }
  860.         rc.left = rc.right ;
  861.         cIteration++ ;
  862.         lpsz = lpNext + 1 ;
  863.     }
  864.  
  865. } // ColumnTextOut()
  866.  
  867. // Stolen from OLEMISC.CPP in the MFC 3.0 source.  Function names
  868. // changed from Afx* to _*.
  869. //
  870. #define _countof(array) (sizeof(array)/sizeof(array[0]))
  871. LPCTSTR AFXAPI _GetScodeString(SCODE sc)
  872. {
  873.     struct SCODE_ENTRY
  874.     {
  875.         SCODE sc;
  876.         LPCTSTR lpszName;
  877.     };
  878.     #define MAKE_SCODE_ENTRY(sc)    { sc, _T(#sc) }
  879.     static const SCODE_ENTRY scNameTable[] =
  880.     {
  881.         MAKE_SCODE_ENTRY(S_OK),
  882.         MAKE_SCODE_ENTRY(S_FALSE),
  883.  
  884.         MAKE_SCODE_ENTRY(CACHE_S_FORMATETC_NOTSUPPORTED),
  885.         MAKE_SCODE_ENTRY(CACHE_S_SAMECACHE),
  886.         MAKE_SCODE_ENTRY(CACHE_S_SOMECACHES_NOTUPDATED),
  887.         MAKE_SCODE_ENTRY(CONVERT10_S_NO_PRESENTATION),
  888.         MAKE_SCODE_ENTRY(DATA_S_SAMEFORMATETC),
  889.         MAKE_SCODE_ENTRY(DRAGDROP_S_CANCEL),
  890.         MAKE_SCODE_ENTRY(DRAGDROP_S_DROP),
  891.         MAKE_SCODE_ENTRY(DRAGDROP_S_USEDEFAULTCURSORS),
  892.         MAKE_SCODE_ENTRY(INPLACE_S_TRUNCATED),
  893.         MAKE_SCODE_ENTRY(MK_S_HIM),
  894.         MAKE_SCODE_ENTRY(MK_S_ME),
  895.         MAKE_SCODE_ENTRY(MK_S_MONIKERALREADYREGISTERED),
  896.         MAKE_SCODE_ENTRY(MK_S_REDUCED_TO_SELF),
  897.         MAKE_SCODE_ENTRY(MK_S_US),
  898.         MAKE_SCODE_ENTRY(OLE_S_MAC_CLIPFORMAT),
  899.         MAKE_SCODE_ENTRY(OLE_S_STATIC),
  900.         MAKE_SCODE_ENTRY(OLE_S_USEREG),
  901.         MAKE_SCODE_ENTRY(OLEOBJ_S_CANNOT_DOVERB_NOW),
  902.         MAKE_SCODE_ENTRY(OLEOBJ_S_INVALIDHWND),
  903.         MAKE_SCODE_ENTRY(OLEOBJ_S_INVALIDVERB),
  904.         MAKE_SCODE_ENTRY(OLEOBJ_S_LAST),
  905.         MAKE_SCODE_ENTRY(STG_S_CONVERTED),
  906.         MAKE_SCODE_ENTRY(VIEW_S_ALREADY_FROZEN),
  907.  
  908.         MAKE_SCODE_ENTRY(E_UNEXPECTED),
  909.         MAKE_SCODE_ENTRY(E_NOTIMPL),
  910.         MAKE_SCODE_ENTRY(E_OUTOFMEMORY),
  911.         MAKE_SCODE_ENTRY(E_INVALIDARG),
  912.         MAKE_SCODE_ENTRY(E_NOINTERFACE),
  913.         MAKE_SCODE_ENTRY(E_POINTER),
  914.         MAKE_SCODE_ENTRY(E_HANDLE),
  915.         MAKE_SCODE_ENTRY(E_ABORT),
  916.         MAKE_SCODE_ENTRY(E_FAIL),
  917.         MAKE_SCODE_ENTRY(E_ACCESSDENIED),
  918.  
  919.         MAKE_SCODE_ENTRY(CACHE_E_NOCACHE_UPDATED),
  920.         MAKE_SCODE_ENTRY(CLASS_E_CLASSNOTAVAILABLE),
  921.         MAKE_SCODE_ENTRY(CLASS_E_NOAGGREGATION),
  922.         MAKE_SCODE_ENTRY(CLIPBRD_E_BAD_DATA),
  923.         MAKE_SCODE_ENTRY(CLIPBRD_E_CANT_CLOSE),
  924.         MAKE_SCODE_ENTRY(CLIPBRD_E_CANT_EMPTY),
  925.         MAKE_SCODE_ENTRY(CLIPBRD_E_CANT_OPEN),
  926.         MAKE_SCODE_ENTRY(CLIPBRD_E_CANT_SET),
  927.         MAKE_SCODE_ENTRY(CO_E_ALREADYINITIALIZED),
  928.         MAKE_SCODE_ENTRY(CO_E_APPDIDNTREG),
  929.         MAKE_SCODE_ENTRY(CO_E_APPNOTFOUND),
  930.         MAKE_SCODE_ENTRY(CO_E_APPSINGLEUSE),
  931.         #ifdef _WIN32
  932.         MAKE_SCODE_ENTRY(CO_E_BAD_PATH),
  933.         #endif
  934.         MAKE_SCODE_ENTRY(CO_E_CANTDETERMINECLASS),
  935.         #ifdef _WIN32
  936.         MAKE_SCODE_ENTRY(CO_E_CLASS_CREATE_FAILED),
  937.         #endif
  938.         MAKE_SCODE_ENTRY(CO_E_CLASSSTRING),
  939.         MAKE_SCODE_ENTRY(CO_E_DLLNOTFOUND),
  940.         MAKE_SCODE_ENTRY(CO_E_ERRORINAPP),
  941.         MAKE_SCODE_ENTRY(CO_E_ERRORINDLL),
  942.         MAKE_SCODE_ENTRY(CO_E_IIDSTRING),
  943.         MAKE_SCODE_ENTRY(CO_E_NOTINITIALIZED),
  944.         MAKE_SCODE_ENTRY(CO_E_OBJISREG),
  945.         MAKE_SCODE_ENTRY(CO_E_OBJNOTCONNECTED),
  946.         MAKE_SCODE_ENTRY(CO_E_OBJNOTREG),
  947.         #ifdef _WIN32
  948.         MAKE_SCODE_ENTRY(CO_E_OBJSRV_RPC_FAILURE),
  949.         MAKE_SCODE_ENTRY(CO_E_SCM_ERROR),
  950.         MAKE_SCODE_ENTRY(CO_E_SCM_RPC_FAILURE),
  951.         MAKE_SCODE_ENTRY(CO_E_SERVER_EXEC_FAILURE),
  952.         MAKE_SCODE_ENTRY(CO_E_SERVER_STOPPING),
  953.         #endif
  954.         MAKE_SCODE_ENTRY(CO_E_WRONGOSFORAPP),
  955.         MAKE_SCODE_ENTRY(CONVERT10_E_OLESTREAM_BITMAP_TO_DIB),
  956.         MAKE_SCODE_ENTRY(CONVERT10_E_OLESTREAM_FMT),
  957.         MAKE_SCODE_ENTRY(CONVERT10_E_OLESTREAM_GET),
  958.         MAKE_SCODE_ENTRY(CONVERT10_E_OLESTREAM_PUT),
  959.         MAKE_SCODE_ENTRY(CONVERT10_E_STG_DIB_TO_BITMAP),
  960.         MAKE_SCODE_ENTRY(CONVERT10_E_STG_FMT),
  961.         MAKE_SCODE_ENTRY(CONVERT10_E_STG_NO_STD_STREAM),
  962.         MAKE_SCODE_ENTRY(DISP_E_ARRAYISLOCKED),
  963.         MAKE_SCODE_ENTRY(DISP_E_BADCALLEE),
  964.         MAKE_SCODE_ENTRY(DISP_E_BADINDEX),
  965.         MAKE_SCODE_ENTRY(DISP_E_BADPARAMCOUNT),
  966.         MAKE_SCODE_ENTRY(DISP_E_BADVARTYPE),
  967.         MAKE_SCODE_ENTRY(DISP_E_EXCEPTION),
  968.         MAKE_SCODE_ENTRY(DISP_E_MEMBERNOTFOUND),
  969.         MAKE_SCODE_ENTRY(DISP_E_NONAMEDARGS),
  970.         MAKE_SCODE_ENTRY(DISP_E_NOTACOLLECTION),
  971.         MAKE_SCODE_ENTRY(DISP_E_OVERFLOW),
  972.         MAKE_SCODE_ENTRY(DISP_E_PARAMNOTFOUND),
  973.         MAKE_SCODE_ENTRY(DISP_E_PARAMNOTOPTIONAL),
  974.         MAKE_SCODE_ENTRY(DISP_E_TYPEMISMATCH),
  975.         MAKE_SCODE_ENTRY(DISP_E_UNKNOWNINTERFACE),
  976.         MAKE_SCODE_ENTRY(DISP_E_UNKNOWNLCID),
  977.         MAKE_SCODE_ENTRY(DISP_E_UNKNOWNNAME),
  978.         MAKE_SCODE_ENTRY(DRAGDROP_E_ALREADYREGISTERED),
  979.         MAKE_SCODE_ENTRY(DRAGDROP_E_INVALIDHWND),
  980.         MAKE_SCODE_ENTRY(DRAGDROP_E_NOTREGISTERED),
  981.         MAKE_SCODE_ENTRY(DV_E_CLIPFORMAT),
  982.         MAKE_SCODE_ENTRY(DV_E_DVASPECT),
  983.         MAKE_SCODE_ENTRY(DV_E_DVTARGETDEVICE),
  984.         MAKE_SCODE_ENTRY(DV_E_DVTARGETDEVICE_SIZE),
  985.         MAKE_SCODE_ENTRY(DV_E_FORMATETC),
  986.         MAKE_SCODE_ENTRY(DV_E_LINDEX),
  987.         MAKE_SCODE_ENTRY(DV_E_NOIVIEWOBJECT),
  988.         MAKE_SCODE_ENTRY(DV_E_STATDATA),
  989.         MAKE_SCODE_ENTRY(DV_E_STGMEDIUM),
  990.         MAKE_SCODE_ENTRY(DV_E_TYMED),
  991.         MAKE_SCODE_ENTRY(INPLACE_E_NOTOOLSPACE),
  992.         MAKE_SCODE_ENTRY(INPLACE_E_NOTUNDOABLE),
  993.         #ifdef _WIN32
  994.         MAKE_SCODE_ENTRY(MEM_E_INVALID_LINK),
  995.         MAKE_SCODE_ENTRY(MEM_E_INVALID_ROOT),
  996.         MAKE_SCODE_ENTRY(MEM_E_INVALID_SIZE),
  997.         #endif
  998.         MAKE_SCODE_ENTRY(MK_E_CANTOPENFILE),
  999.         MAKE_SCODE_ENTRY(MK_E_CONNECTMANUALLY),
  1000.         #ifdef _WIN32
  1001.         MAKE_SCODE_ENTRY(MK_E_ENUMERATION_FAILED),
  1002.         #endif
  1003.         MAKE_SCODE_ENTRY(MK_E_EXCEEDEDDEADLINE),
  1004.         MAKE_SCODE_ENTRY(MK_E_INTERMEDIATEINTERFACENOTSUPPORTED),
  1005.         MAKE_SCODE_ENTRY(MK_E_INVALIDEXTENSION),
  1006.         MAKE_SCODE_ENTRY(MK_E_MUSTBOTHERUSER),
  1007.         MAKE_SCODE_ENTRY(MK_E_NEEDGENERIC),
  1008.         #ifdef _WIN32
  1009.         MAKE_SCODE_ENTRY(MK_E_NO_NORMALIZED),
  1010.         #endif
  1011.         MAKE_SCODE_ENTRY(MK_E_NOINVERSE),
  1012.         MAKE_SCODE_ENTRY(MK_E_NOOBJECT),
  1013.         MAKE_SCODE_ENTRY(MK_E_NOPREFIX),
  1014.         MAKE_SCODE_ENTRY(MK_E_NOSTORAGE),
  1015.         MAKE_SCODE_ENTRY(MK_E_NOTBINDABLE),
  1016.         MAKE_SCODE_ENTRY(MK_E_NOTBOUND),
  1017.         MAKE_SCODE_ENTRY(MK_E_SYNTAX),
  1018.         MAKE_SCODE_ENTRY(MK_E_UNAVAILABLE),
  1019.         MAKE_SCODE_ENTRY(OLE_E_ADVF),
  1020.         MAKE_SCODE_ENTRY(OLE_E_ADVISENOTSUPPORTED),
  1021.         MAKE_SCODE_ENTRY(OLE_E_BLANK),
  1022.         MAKE_SCODE_ENTRY(OLE_E_CANT_BINDTOSOURCE),
  1023.         MAKE_SCODE_ENTRY(OLE_E_CANT_GETMONIKER),
  1024.         MAKE_SCODE_ENTRY(OLE_E_CANTCONVERT),
  1025.         MAKE_SCODE_ENTRY(OLE_E_CLASSDIFF),
  1026.         MAKE_SCODE_ENTRY(OLE_E_ENUM_NOMORE),
  1027.         MAKE_SCODE_ENTRY(OLE_E_INVALIDHWND),
  1028.         MAKE_SCODE_ENTRY(OLE_E_INVALIDRECT),
  1029.         MAKE_SCODE_ENTRY(OLE_E_NOCACHE),
  1030.         MAKE_SCODE_ENTRY(OLE_E_NOCONNECTION),
  1031.         MAKE_SCODE_ENTRY(OLE_E_NOSTORAGE),
  1032.         MAKE_SCODE_ENTRY(OLE_E_NOT_INPLACEACTIVE),
  1033.         MAKE_SCODE_ENTRY(OLE_E_NOTRUNNING),
  1034.         MAKE_SCODE_ENTRY(OLE_E_OLEVERB),
  1035.         MAKE_SCODE_ENTRY(OLE_E_PROMPTSAVECANCELLED),
  1036.         MAKE_SCODE_ENTRY(OLE_E_STATIC),
  1037.         MAKE_SCODE_ENTRY(OLE_E_WRONGCOMPOBJ),
  1038.         MAKE_SCODE_ENTRY(OLEOBJ_E_INVALIDVERB),
  1039.         MAKE_SCODE_ENTRY(OLEOBJ_E_NOVERBS),
  1040.         MAKE_SCODE_ENTRY(REGDB_E_CLASSNOTREG),
  1041.         MAKE_SCODE_ENTRY(REGDB_E_IIDNOTREG),
  1042.         MAKE_SCODE_ENTRY(REGDB_E_INVALIDVALUE),
  1043.         MAKE_SCODE_ENTRY(REGDB_E_KEYMISSING),
  1044.         MAKE_SCODE_ENTRY(REGDB_E_READREGDB),
  1045.         MAKE_SCODE_ENTRY(REGDB_E_WRITEREGDB),
  1046.         #ifdef _WIN32
  1047.         MAKE_SCODE_ENTRY(RPC_E_ATTEMPTED_MULTITHREAD),
  1048.         #endif
  1049.         MAKE_SCODE_ENTRY(RPC_E_CALL_CANCELED),
  1050.         MAKE_SCODE_ENTRY(RPC_E_CALL_REJECTED),
  1051.         MAKE_SCODE_ENTRY(RPC_E_CANTCALLOUT_AGAIN),
  1052.         MAKE_SCODE_ENTRY(RPC_E_CANTCALLOUT_INASYNCCALL),
  1053.         MAKE_SCODE_ENTRY(RPC_E_CANTCALLOUT_INEXTERNALCALL),
  1054.         #ifdef _WIN32
  1055.         MAKE_SCODE_ENTRY(RPC_E_CANTCALLOUT_ININPUTSYNCCALL),
  1056.         #endif
  1057.         MAKE_SCODE_ENTRY(RPC_E_CANTPOST_INSENDCALL),
  1058.         MAKE_SCODE_ENTRY(RPC_E_CANTTRANSMIT_CALL),
  1059.         #ifdef _WIN32
  1060.         MAKE_SCODE_ENTRY(RPC_E_CHANGED_MODE),
  1061.         #endif
  1062.         MAKE_SCODE_ENTRY(RPC_E_CLIENT_CANTMARSHAL_DATA),
  1063.         MAKE_SCODE_ENTRY(RPC_E_CLIENT_CANTUNMARSHAL_DATA),
  1064.         MAKE_SCODE_ENTRY(RPC_E_CLIENT_DIED),
  1065.         MAKE_SCODE_ENTRY(RPC_E_CONNECTION_TERMINATED),
  1066.         #ifdef _WIN32
  1067.         MAKE_SCODE_ENTRY(RPC_E_DISCONNECTED),
  1068.         MAKE_SCODE_ENTRY(RPC_E_FAULT),
  1069.         MAKE_SCODE_ENTRY(RPC_E_INVALID_CALLDATA),
  1070.         #endif
  1071.         MAKE_SCODE_ENTRY(RPC_E_INVALID_DATA),
  1072.         MAKE_SCODE_ENTRY(RPC_E_INVALID_DATAPACKET),
  1073.         MAKE_SCODE_ENTRY(RPC_E_INVALID_PARAMETER),
  1074.         #ifdef _WIN32
  1075.         MAKE_SCODE_ENTRY(RPC_E_INVALIDMETHOD),
  1076.         MAKE_SCODE_ENTRY(RPC_E_NOT_REGISTERED),
  1077.         MAKE_SCODE_ENTRY(RPC_E_OUT_OF_RESOURCES),
  1078.         MAKE_SCODE_ENTRY(RPC_E_RETRY),
  1079.         #endif
  1080.         MAKE_SCODE_ENTRY(RPC_E_SERVER_CANTMARSHAL_DATA),
  1081.         MAKE_SCODE_ENTRY(RPC_E_SERVER_CANTUNMARSHAL_DATA),
  1082.         MAKE_SCODE_ENTRY(RPC_E_SERVER_DIED),
  1083.         #ifdef _WIN32
  1084.         MAKE_SCODE_ENTRY(RPC_E_SERVER_DIED_DNE),
  1085.         MAKE_SCODE_ENTRY(RPC_E_SERVERCALL_REJECTED),
  1086.         MAKE_SCODE_ENTRY(RPC_E_SERVERCALL_RETRYLATER),
  1087.         MAKE_SCODE_ENTRY(RPC_E_SERVERFAULT),
  1088.         MAKE_SCODE_ENTRY(RPC_E_SYS_CALL_FAILED),
  1089.         MAKE_SCODE_ENTRY(RPC_E_THREAD_NOT_INIT),
  1090.         MAKE_SCODE_ENTRY(RPC_E_WRONG_THREAD),
  1091.         #endif
  1092.         MAKE_SCODE_ENTRY(RPC_E_UNEXPECTED),
  1093.         MAKE_SCODE_ENTRY(STG_E_ABNORMALAPIEXIT),
  1094.         MAKE_SCODE_ENTRY(STG_E_ACCESSDENIED),
  1095.         MAKE_SCODE_ENTRY(STG_E_CANTSAVE),
  1096.         MAKE_SCODE_ENTRY(STG_E_DISKISWRITEPROTECTED),
  1097.         MAKE_SCODE_ENTRY(STG_E_EXTANTMARSHALLINGS),
  1098.         MAKE_SCODE_ENTRY(STG_E_FILEALREADYEXISTS),
  1099.         MAKE_SCODE_ENTRY(STG_E_FILENOTFOUND),
  1100.         MAKE_SCODE_ENTRY(STG_E_INSUFFICIENTMEMORY),
  1101.         MAKE_SCODE_ENTRY(STG_E_INUSE),
  1102.         MAKE_SCODE_ENTRY(STG_E_INVALIDFLAG),
  1103.         MAKE_SCODE_ENTRY(STG_E_INVALIDFUNCTION),
  1104.         MAKE_SCODE_ENTRY(STG_E_INVALIDHANDLE),
  1105.         MAKE_SCODE_ENTRY(STG_E_INVALIDHEADER),
  1106.         MAKE_SCODE_ENTRY(STG_E_INVALIDNAME),
  1107.         MAKE_SCODE_ENTRY(STG_E_INVALIDPARAMETER),
  1108.         MAKE_SCODE_ENTRY(STG_E_INVALIDPOINTER),
  1109.         MAKE_SCODE_ENTRY(STG_E_LOCKVIOLATION),
  1110.         MAKE_SCODE_ENTRY(STG_E_MEDIUMFULL),
  1111.         MAKE_SCODE_ENTRY(STG_E_NOMOREFILES),
  1112.         MAKE_SCODE_ENTRY(STG_E_NOTCURRENT),
  1113.         MAKE_SCODE_ENTRY(STG_E_NOTFILEBASEDSTORAGE),
  1114.         MAKE_SCODE_ENTRY(STG_E_OLDDLL),
  1115.         MAKE_SCODE_ENTRY(STG_E_OLDFORMAT),
  1116.         MAKE_SCODE_ENTRY(STG_E_PATHNOTFOUND),
  1117.         MAKE_SCODE_ENTRY(STG_E_READFAULT),
  1118.         MAKE_SCODE_ENTRY(STG_E_REVERTED),
  1119.         MAKE_SCODE_ENTRY(STG_E_SEEKERROR),
  1120.         MAKE_SCODE_ENTRY(STG_E_SHAREREQUIRED),
  1121.         MAKE_SCODE_ENTRY(STG_E_SHAREVIOLATION),
  1122.         MAKE_SCODE_ENTRY(STG_E_TOOMANYOPENFILES),
  1123.         MAKE_SCODE_ENTRY(STG_E_UNIMPLEMENTEDFUNCTION),
  1124.         MAKE_SCODE_ENTRY(STG_E_UNKNOWN),
  1125.         MAKE_SCODE_ENTRY(STG_E_WRITEFAULT),
  1126.         MAKE_SCODE_ENTRY(TYPE_E_AMBIGUOUSNAME),
  1127.         MAKE_SCODE_ENTRY(TYPE_E_BADMODULEKIND),
  1128.         MAKE_SCODE_ENTRY(TYPE_E_BUFFERTOOSMALL),
  1129.         MAKE_SCODE_ENTRY(TYPE_E_CANTCREATETMPFILE),
  1130.         MAKE_SCODE_ENTRY(TYPE_E_CANTLOADLIBRARY),
  1131.         MAKE_SCODE_ENTRY(TYPE_E_CIRCULARTYPE),
  1132.         MAKE_SCODE_ENTRY(TYPE_E_DLLFUNCTIONNOTFOUND),
  1133.         MAKE_SCODE_ENTRY(TYPE_E_DUPLICATEID),
  1134.         MAKE_SCODE_ENTRY(TYPE_E_ELEMENTNOTFOUND),
  1135.         MAKE_SCODE_ENTRY(TYPE_E_INCONSISTENTPROPFUNCS),
  1136.         MAKE_SCODE_ENTRY(TYPE_E_INVALIDSTATE),
  1137.         MAKE_SCODE_ENTRY(TYPE_E_INVDATAREAD),
  1138.         MAKE_SCODE_ENTRY(TYPE_E_IOERROR),
  1139.         MAKE_SCODE_ENTRY(TYPE_E_LIBNOTREGISTERED),
  1140.         MAKE_SCODE_ENTRY(TYPE_E_NAMECONFLICT),
  1141.         MAKE_SCODE_ENTRY(TYPE_E_OUTOFBOUNDS),
  1142.         MAKE_SCODE_ENTRY(TYPE_E_QUALIFIEDNAMEDISALLOWED),
  1143.         MAKE_SCODE_ENTRY(TYPE_E_REGISTRYACCESS),
  1144.         MAKE_SCODE_ENTRY(TYPE_E_SIZETOOBIG),
  1145.         MAKE_SCODE_ENTRY(TYPE_E_TYPEMISMATCH),
  1146.         MAKE_SCODE_ENTRY(TYPE_E_UNDEFINEDTYPE),
  1147.         MAKE_SCODE_ENTRY(TYPE_E_UNKNOWNLCID),
  1148.         MAKE_SCODE_ENTRY(TYPE_E_UNSUPFORMAT),
  1149.         MAKE_SCODE_ENTRY(TYPE_E_WRONGTYPEKIND),
  1150.         MAKE_SCODE_ENTRY(VIEW_E_DRAW),
  1151.     };
  1152.     #undef MAKE_SCODE_ENTRY
  1153.  
  1154.     // look for it in the table
  1155.     for (int i = 0; i < _countof(scNameTable); i++)
  1156.     {
  1157.         if (sc == scNameTable[i].sc)
  1158.             return scNameTable[i].lpszName;
  1159.     }
  1160.     return NULL;    // not found
  1161. }
  1162.  
  1163. LPCTSTR AFXAPI _GetScodeRangeString(SCODE sc)
  1164. {
  1165.     struct RANGE_ENTRY
  1166.     {
  1167.         SCODE scFirst;
  1168.         SCODE scLast;
  1169.         LPCTSTR lpszName;
  1170.     };
  1171.     #define MAKE_RANGE_ENTRY(scRange) \
  1172.         { scRange##_FIRST, scRange##_LAST, \
  1173.             _T(#scRange) _T("_FIRST...") _T(#scRange) _T("_LAST") }
  1174.  
  1175.     static const RANGE_ENTRY scRangeTable[] =
  1176.     {
  1177.         MAKE_RANGE_ENTRY(CACHE_E),
  1178.         MAKE_RANGE_ENTRY(CACHE_S),
  1179.         MAKE_RANGE_ENTRY(CLASSFACTORY_E),
  1180.         MAKE_RANGE_ENTRY(CLASSFACTORY_S),
  1181.         MAKE_RANGE_ENTRY(CLIENTSITE_E),
  1182.         MAKE_RANGE_ENTRY(CLIENTSITE_S),
  1183.         MAKE_RANGE_ENTRY(CLIPBRD_E),
  1184.         MAKE_RANGE_ENTRY(CLIPBRD_S),
  1185.         MAKE_RANGE_ENTRY(CONVERT10_E),
  1186.         MAKE_RANGE_ENTRY(CONVERT10_S),
  1187.         MAKE_RANGE_ENTRY(CO_E),
  1188.         MAKE_RANGE_ENTRY(CO_S),
  1189.         MAKE_RANGE_ENTRY(DATA_E),
  1190.         MAKE_RANGE_ENTRY(DATA_S),
  1191.         MAKE_RANGE_ENTRY(DRAGDROP_E),
  1192.         MAKE_RANGE_ENTRY(DRAGDROP_S),
  1193.         MAKE_RANGE_ENTRY(ENUM_E),
  1194.         MAKE_RANGE_ENTRY(ENUM_S),
  1195.         MAKE_RANGE_ENTRY(INPLACE_E),
  1196.         MAKE_RANGE_ENTRY(INPLACE_S),
  1197.         MAKE_RANGE_ENTRY(MARSHAL_E),
  1198.         MAKE_RANGE_ENTRY(MARSHAL_S),
  1199.         MAKE_RANGE_ENTRY(MK_E),
  1200.         MAKE_RANGE_ENTRY(MK_S),
  1201.         MAKE_RANGE_ENTRY(OLEOBJ_E),
  1202.         MAKE_RANGE_ENTRY(OLEOBJ_S),
  1203.         MAKE_RANGE_ENTRY(OLE_E),
  1204.         MAKE_RANGE_ENTRY(OLE_S),
  1205.         MAKE_RANGE_ENTRY(REGDB_E),
  1206.         MAKE_RANGE_ENTRY(REGDB_S),
  1207.         MAKE_RANGE_ENTRY(VIEW_E),
  1208.         MAKE_RANGE_ENTRY(VIEW_S),
  1209.     };
  1210.     #undef MAKE_RANGE_ENTRY
  1211.  
  1212.     // look for it in the table
  1213.     for (int i = 0; i < _countof(scRangeTable); i++)
  1214.     {
  1215.         if (sc >= scRangeTable[i].scFirst && sc <= scRangeTable[i].scLast)
  1216.             return scRangeTable[i].lpszName;
  1217.     }
  1218.     return NULL;    // not found
  1219. }
  1220.  
  1221. LPCTSTR AFXAPI _GetSeverityString(SCODE sc)
  1222. {
  1223.     static const TCHAR* rgszSEVERITY[] =
  1224.     {
  1225.         _T("SEVERITY_SUCCESS"),
  1226.         _T("SEVERITY_ERROR"),
  1227.     };
  1228.     return rgszSEVERITY[SCODE_SEVERITY(sc)];
  1229. }
  1230.  
  1231. LPCTSTR AFXAPI _GetFacilityString(SCODE sc)
  1232. {
  1233.     static const TCHAR* rgszFACILITY[] =
  1234.     {
  1235.         _T("FACILITY_NULL"),
  1236.         _T("FACILITY_RPC"),
  1237.         _T("FACILITY_DISPATCH"),
  1238.         _T("FACILITY_STORAGE"),
  1239.         _T("FACILITY_ITF"),
  1240.         _T("FACILITY_0x05"),
  1241.         _T("FACILITY_0x06"),
  1242.         _T("FACILITY_WIN32"),
  1243.         _T("FACILITY_WINDOWS"),
  1244.     };
  1245.     if (SCODE_FACILITY(sc) >= _countof(rgszFACILITY))
  1246.         return _T("<Unknown Facility>");
  1247.  
  1248.     return rgszFACILITY[SCODE_FACILITY(sc)];
  1249. }
  1250.  
  1251. LPCTSTR AFXAPI _GetFullScodeString(SCODE sc)
  1252. {
  1253.     static TCHAR szBuf[128];
  1254.     LPCTSTR lpsz;
  1255.     if ((lpsz = _GetScodeString(sc)) != NULL)
  1256.     {
  1257.         // found exact match
  1258.         wsprintf(szBuf, _T("%s ($%08lX)"), lpsz, sc);
  1259.     }
  1260.     else if ((lpsz = _GetScodeRangeString(sc)) != NULL)
  1261.     {
  1262.         // found suitable range
  1263.         wsprintf(szBuf, _T("range: %s ($%08lX)"), lpsz, sc);
  1264.     }
  1265.     else
  1266.     {
  1267.         // not found at all -- split it up into its parts
  1268.         wsprintf(szBuf, _T("severity: %s, facility: %s ($%08lX)"),
  1269.             _GetSeverityString(sc), _GetFacilityString(sc), sc);
  1270.     }
  1271.     return szBuf;
  1272. }
  1273.  
  1274.  
  1275. LPTSTR HRtoString( HRESULT hr )
  1276. {
  1277.     SCODE   sc ;
  1278.     sc = GetScode( hr ) ;
  1279.  
  1280. #ifdef FORMATMSG
  1281.     LPVOID lpMessageBuffer ;
  1282.     if (FormatMessage(
  1283.           FORMAT_MESSAGE_ALLOCATE_BUFFER |
  1284.           FORMAT_MESSAGE_FROM_SYSTEM,
  1285.           NULL,
  1286.           sc,
  1287.           MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
  1288.           (LPTSTR) &lpMessageBuffer,
  1289.           0,
  1290.           NULL ))
  1291.     {
  1292.         wsprintf( sz, _T( "%s (0x%lx)" ), (LPTSTR)lpMessageBuffer, sc);
  1293.         LocalFree(lpMessageBuffer) ;
  1294.     }
  1295.     else
  1296.     {
  1297.         wsprintf( sz, _T( "Unknown Scode (0x%lx)" ), sc);
  1298.     }
  1299.  
  1300.  
  1301. #endif // !FORMATMSG
  1302.  
  1303.     return (LPTSTR)_GetFullScodeString(sc) ;
  1304. }
  1305.  
  1306. static TCHAR * g_rgszVT[] =
  1307. {
  1308.     _T( "Void" ),             //VT_EMPTY           = 0,   /* [V]   [P]  nothing                     */
  1309.     _T( "Null" ),             //VT_NULL            = 1,   /* [V]        SQL style Null              */
  1310.     _T( "Integer" ),          //VT_I2              = 2,   /* [V][T][P]  2 byte signed int           */
  1311.     _T( "Long" ),             //VT_I4              = 3,   /* [V][T][P]  4 byte signed int           */
  1312.     _T( "Single" ),           //VT_R4              = 4,   /* [V][T][P]  4 byte real                 */
  1313.     _T( "Double" ),           //VT_R8              = 5,   /* [V][T][P]  8 byte real                 */
  1314.     _T( "Currency" ),         //VT_CY              = 6,   /* [V][T][P]  currency                    */
  1315.     _T( "Date" ),             //VT_DATE            = 7,   /* [V][T][P]  date                        */
  1316.     _T( "String" ),           //VT_BSTR            = 8,   /* [V][T][P]  binary string               */
  1317.     _T( "Object" ),           //VT_DISPATCH        = 9,   /* [V][T]     IDispatch FAR*              */
  1318.     _T( "SCODE" ),            //VT_ERROR           = 10,  /* [V][T]     SCODE                       */
  1319.     _T( "Boolean" ),          //VT_BOOL            = 11,  /* [V][T][P]  True=-1, False=0            */
  1320.     _T( "Variant" ),          //VT_VARIANT         = 12,  /* [V][T][P]  VARIANT FAR*                */
  1321.     _T( "pIUnknown" ),        //VT_UNKNOWN         = 13,  /* [V][T]     IUnknown FAR*               */
  1322.     _T( "Unicode" ),          //VT_WBSTR           = 14,  /* [V][T]     wide binary string          */
  1323.     _T( "" ),                 //                   = 15,
  1324.     _T( "BYTE" ),             //VT_I1              = 16,  /*    [T]     signed char                 */
  1325.     _T( "char" ),             //VT_UI1             = 17,  /*    [T]     unsigned char               */
  1326.     _T( "USHORT" ),           //VT_UI2             = 18,  /*    [T]     unsigned short              */
  1327.     _T( "ULONG" ),            //VT_UI4             = 19,  /*    [T]     unsigned short              */
  1328.     _T( "int64" ),            //VT_I8              = 20,  /*    [T][P]  signed 64-bit int           */
  1329.     _T( "uint64" ),           //VT_UI8             = 21,  /*    [T]     unsigned 64-bit int         */
  1330.     _T( "int" ),              //VT_INT             = 22,  /*    [T]     signed machine int          */
  1331.     _T( "UINT" ),             //VT_UINT            = 23,  /*    [T]     unsigned machine int        */
  1332.     _T( "VOID" ),             //VT_VOID            = 24,  /*    [T]     C style void                */
  1333.     _T( "HRESULT" ),          //VT_HRESULT         = 25,  /*    [T]                                 */
  1334.     _T( "PTR" ),              //VT_PTR             = 26,  /*    [T]     pointer type                */
  1335.     _T( "SAFEARRAY" ),        //VT_SAFEARRAY       = 27,  /*    [T]     (use VT_ARRAY in VARIANT)   */
  1336.     _T( "CARRAY" ),           //VT_CARRAY          = 28,  /*    [T]     C style array               */
  1337.     _T( "USERDEFINED" ),      //VT_USERDEFINED     = 29,  /*    [T]     user defined type         */
  1338.     _T( "LPTSTR" ),            //VT_LPTSTR           = 30,  /*    [T][P]  null terminated string      */
  1339.     _T( "LPWSTR" ),           //VT_LPWSTR          = 31,  /*    [T][P]  wide null terminated string */
  1340.     _T( "" ),                 //                   = 32,
  1341.     _T( "" ),                 //                   = 33,
  1342.     _T( "" ),                 //                   = 34,
  1343.     _T( "" ),                 //                   = 35,
  1344.     _T( "" ),                 //                   = 36,
  1345.     _T( "" ),                 //                   = 37,
  1346.     _T( "" ),                 //                   = 38,
  1347.     _T( "" ),                 //                   = 39,
  1348.     _T( "" ),                 //                   = 40,
  1349.     _T( "" ),                 //                   = 41,
  1350.     _T( "" ),                 //                   = 42,
  1351.     _T( "" ),                 //                   = 43,
  1352.     _T( "" ),                 //                   = 44,
  1353.     _T( "" ),                 //                   = 45,
  1354.     _T( "" ),                 //                   = 46,
  1355.     _T( "" ),                 //                   = 47,
  1356.     _T( "" ),                 //                   = 48,
  1357.     _T( "" ),                 //                   = 49,
  1358.     _T( "" ),                 //                   = 50,
  1359.     _T( "" ),                 //                   = 51,
  1360.     _T( "" ),                 //                   = 52,
  1361.     _T( "" ),                 //                   = 53,
  1362.     _T( "" ),                 //                   = 54,
  1363.     _T( "" ),                 //                   = 55,
  1364.     _T( "" ),                 //                   = 56,
  1365.     _T( "" ),                 //                   = 57,
  1366.     _T( "" ),                 //                   = 58,
  1367.     _T( "" ),                 //                   = 59,
  1368.     _T( "" ),                 //                   = 60,
  1369.     _T( "" ),                 //                   = 61,
  1370.     _T( "" ),                 //                   = 62,
  1371.     _T( "" ),                 //                   = 63,
  1372.     _T( "FILETIME" ),         //VT_FILETIME        = 64,  /*       [P]  FILETIME                    */
  1373.     _T( "BLOB" ),             //VT_BLOB            = 65,  /*       [P]  Length prefixed bytes       */
  1374.     _T( "STREAM" ),           //VT_STREAM          = 66,  /*       [P]  Name of the stream follows  */
  1375.     _T( "STORAGE" ),          //VT_STORAGE         = 67,  /*       [P]  Name of the storage follows */
  1376.     _T( "STREAMED_OBJECT" ),  //VT_STREAMED_OBJECT = 68,  /*       [P]  Stream contains an object   */
  1377.     _T( "STORED_OBJECT" ),    //VT_STORED_OBJECT   = 69,  /*       [P]  Storage contains an object  */
  1378.     _T( "BLOB_OBJECT" ),      //VT_BLOB_OBJECT     = 70,  /*       [P]  Blob contains an object     */
  1379.     _T( "CF" ),               //VT_CF              = 71,  /*       [P]  Clipboard format            */
  1380.     _T( "CLSID" ),            //VT_CLSID           = 72   /*       [P]  A Class ID                  */
  1381. };
  1382.  
  1383. LPTSTR VTtoString( VARTYPE vt )
  1384. {
  1385.     static TCHAR szBuf[64];
  1386.  
  1387.     if (vt <= VT_CLSID)
  1388.         return (LPTSTR)g_rgszVT[vt] ;
  1389.  
  1390.     if (vt & VT_VECTOR)
  1391.     {
  1392.         vt &= ~VT_VECTOR ;
  1393.         if (vt <= VT_CLSID)
  1394.             wsprintf( szBuf, _T("VECTOR of %s"), (LPTSTR)g_rgszVT[vt] ) ;
  1395.         else
  1396.             wsprintf( szBuf, _T("<Unknown %08lX>"), vt & VT_VECTOR ) ;
  1397.         return (LPTSTR)szBuf ;
  1398.     }
  1399.  
  1400.     if (vt & VT_ARRAY)
  1401.     {
  1402.         vt &= ~VT_ARRAY ;
  1403.         if (vt <= VT_CLSID)
  1404.             wsprintf( szBuf, _T("Array of %s"), (LPTSTR)g_rgszVT[vt] ) ;
  1405.         else
  1406.             wsprintf( szBuf, _T("<Unknown %08lX>"), vt & VT_ARRAY ) ;
  1407.         return (LPTSTR)szBuf ;
  1408.     }
  1409.  
  1410.     if (vt & VT_BYREF)
  1411.     {
  1412.         vt &= ~VT_BYREF ;
  1413.         if (vt <= VT_CLSID)
  1414.             wsprintf( szBuf, _T("%s BYREF "), (LPTSTR)g_rgszVT[vt] ) ;
  1415.         else
  1416.             wsprintf( szBuf, _T("<Unknown %08lX>"), vt & VT_BYREF ) ;
  1417.         return (LPTSTR)szBuf ;
  1418.     }
  1419.  
  1420.     if (vt & VT_RESERVED)
  1421.     {
  1422.         vt &= ~VT_RESERVED ;
  1423.         if (vt <= VT_CLSID)
  1424.             wsprintf( szBuf, _T("RESERVED (%s)"), (LPTSTR)g_rgszVT[vt] ) ;
  1425.         else
  1426.             wsprintf( szBuf, _T("<Unknown %08lX>"), vt & VT_RESERVED ) ;
  1427.         return (LPTSTR)szBuf ;
  1428.     }
  1429.  
  1430.     wsprintf( szBuf, _T("<Unknown %08lX>"), vt ) ;
  1431.  
  1432.     return (LPTSTR)szBuf ;
  1433. }
  1434.  
  1435. /* Remove the filespec portion from a path (including the backslash).
  1436.  */
  1437. VOID WINAPI StripFilespec( LPTSTR lpszPath )
  1438. {
  1439.     LPTSTR     p;
  1440.  
  1441.     p = lpszPath + lstrlen( lpszPath ) ;
  1442.     while ((*p != '\\') && (*p != ':') && (p != lpszPath))
  1443.         p-- ;
  1444.  
  1445.     if (*p == ':')
  1446.         p++ ;
  1447.  
  1448.     /* Don't strip backslash from root directory entry. */
  1449.     if (p != lpszPath)
  1450.     {
  1451.         if ((*p == '\\') && (*(p-1) == ':'))
  1452.             p++ ;
  1453.     }
  1454.  
  1455.     *p = NULL ;
  1456. }
  1457.  
  1458. /* Ensures that a path ends with a backslash.
  1459.  */
  1460. VOID WINAPI AddBackslash( LPTSTR lpszPath )
  1461. {
  1462.     if (lpszPath[lstrlen( lpszPath ) - 1] == '\\')
  1463.         return ;
  1464.     lstrcat( lpszPath, _T("\\") ) ;
  1465. }
  1466.