/*************************************************************************
**
** The following API's are now OBSOLETE because equivalent API's have been
** added to the OLE2.DLL library
**      GetIconOfFile       superceeded by OleGetIconOfFile
**      GetIconOfClass      superceeded by OleGetIconOfClass
**      OleUIMetafilePictFromIconAndLabel
**                          superceeded by OleMetafilePictFromIconAndLabel
*************************************************************************/

/*
 *  GETICON.C
 *
 *  Functions to create DVASPECT_ICON metafile from filename or classname.
 *
 *  GetIconOfFile
 *  GetIconOfClass
 *  OleUIMetafilePictFromIconAndLabel
 *  HIconAndSourceFromClass Extracts the first icon in a class's server path
 *                          and returns the path and icon index to caller.
 *  FIconFileFromClass      Retrieves the path to the exe/dll containing the
 *                           default icon, and the index of the icon.
 *  OleStdIconLabelTextOut  Draw icon label text (line break if necessary)
 *
 *    (c) Copyright Microsoft Corp. 1992-1993 All Rights Reserved
 */


/*******
 *
 * ICON (DVASPECT_ICON) METAFILE FORMAT:
 *
 * The metafile generated with OleUIMetafilePictFromIconAndLabel contains
 * the following records which are used by the functions in DRAWICON.C
 * to draw the icon with and without the label and to extract the icon,
 * label, and icon source/index.
 *
 *  SetWindowOrg
 *  SetWindowExt
 *  DrawIcon:
 *      Inserts records of DIBBITBLT or DIBSTRETCHBLT, once for the
 *      AND mask, one for the image bits.
 *  Escape with the comment "IconOnly"
 *      This indicates where to stop record enumeration to draw only
 *      the icon.
 *  SetTextColor
 *  SetTextAlign
 *  SetBkColor
 *  CreateFont
 *  SelectObject on the font.
 *  ExtTextOut
 *      One or more ExtTextOuts occur if the label is wrapped.  The
 *      text in these records is used to extract the label.
 *  SelectObject on the old font.
 *  DeleteObject on the font.
 *  Escape with a comment that contains the path to the icon source.
 *  Escape with a comment that is the ASCII of the icon index.
 *
 *******/

#define STRICT  1
#include "ole2ui.h"

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <commdlg.h>
#include <memory.h>
#include <cderr.h>
#include "common.h"
#include "utility.h"

static char szSeparators[] = " \t\\/!:";

#define IS_SEPARATOR(c)         ( (c) == ' ' || (c) == '\\' || (c) == '/' || (c) == '\t' || (c) == '!' || (c) == ':' )
#define IS_FILENAME_DELIM(c)    ( (c) == '\\' || (c) == '/' || (c) == ':' )


#if defined( OBSOLETE )
static HINSTANCE  s_hInst;

static char szMaxWidth[] ="WWWWWWWWWW";

//Strings for metafile comments.
static char szIconOnly[]="IconOnly";        //Where to stop to exclude label.


static char szOLE2DLL[] = "ole2.dll";   // name of OLE 2.0 library

#define ICONINDEX              0

#define AUXUSERTYPE_SHORTNAME  USERCLASSTYPE_SHORT  // short name
#define HIMETRIC_PER_INCH   2540      // number HIMETRIC units per inch
#define PTS_PER_INCH          72      // number points (font size) per inch

#define MAP_PIX_TO_LOGHIM(x,ppli)   MulDiv(HIMETRIC_PER_INCH, (x), (ppli))
#define MAP_LOGHIM_TO_PIX(x,ppli)   MulDiv((ppli), (x), HIMETRIC_PER_INCH)

static char szVanillaDocIcon[] = "DefIcon";

static char szDocument[40] = "";


/*
 * GetIconOfFile(HINSTANCE hInst, LPSTR lpszPath, BOOL fUseFileAsLabel)
 *
 * Purpose:
 *  Returns a hMetaPict containing an icon and label (filename) for the
 *  specified filename.
 *
 * Parameters:
 *  hinst
 *  lpszPath        LPSTR path including filename to use
 *  fUseFileAsLabel BOOL TRUE if the icon's label is the filename, FALSE if
 *                  there should be no label.
 *
 * Return Value:
 *  HGLOBAL         hMetaPict containing the icon and label - if there's no
 *                  class in reg db for the file in lpszPath, then we use
 *                  Document.  If lpszPath is NULL, then we return NULL.
 */

STDAPI_(HGLOBAL) GetIconOfFile(HINSTANCE hInst, LPSTR lpszPath, BOOL fUseFileAsLabel)
{
  char     szIconFile[OLEUI_CCHPATHMAX];
  char     szLabel[OLEUI_CCHLABELMAX];
  LPSTR    lpszClsid = NULL;
  CLSID    clsid;
  HICON    hDefIcon = NULL;
  UINT     IconIndex = 0;
  HGLOBAL  hMetaPict;
  HRESULT  hResult;

  if (NULL == lpszPath)  // even if fUseFileAsLabel is FALSE, we still
	return NULL;             // need a valid filename to get the class.

  s_hInst = hInst;

  hResult = GetClassFile(lpszPath, &clsid);

  if (NOERROR == hResult)  // use the clsid we got to get to the icon
  {
	  hDefIcon = HIconAndSourceFromClass(&clsid,
										 (LPSTR)szIconFile,
										 &IconIndex);
  }

  if ( (NOERROR != hResult) || (NULL == hDefIcon) )
  {
	 // Here, either GetClassFile failed or HIconAndSourceFromClass failed.

	 LPSTR lpszTemp;

	 lpszTemp = lpszPath;

	 while ((*lpszTemp != '.') && (*lpszTemp != '\0'))
		lpszTemp++;


	 if ('.' != *lpszTemp)
	   goto UseVanillaDocument;


	 if (FALSE == GetAssociatedExecutable(lpszTemp, (LPSTR)szIconFile))
	   goto UseVanillaDocument;

	 hDefIcon = ExtractIcon(s_hInst, szIconFile, IconIndex);
  }

  if (hDefIcon <= (HICON)1) // ExtractIcon returns 1 if szExecutable is not exe,
  {                         // 0 if there are no icons.
UseVanillaDocument:

	lstrcpy((LPSTR)szIconFile, (LPSTR)szOLE2DLL);
	IconIndex = ICONINDEX;
	hDefIcon = ExtractIcon(s_hInst, szIconFile, IconIndex);

  }

  // Now let's get the label we want to use.

  if (fUseFileAsLabel)   // strip off path, so we just have the filename.
  {
	 int istrlen;
	 LPSTR lpszBeginFile;

	 istrlen = lstrlen(lpszPath);

	 // set pointer to END of path, so we can walk backwards through it.
	 lpszBeginFile = lpszPath + istrlen -1;

	 while ( (lpszBeginFile >= lpszPath)
			 && (!IS_FILENAME_DELIM(*lpszBeginFile)) )
	  lpszBeginFile--;


	 lpszBeginFile++;  // step back over the delimiter


	 LSTRCPYN(szLabel, lpszBeginFile, sizeof(szLabel));
  }

  else   // use the short user type (AuxUserType2) for the label
  {

	  if (0 == OleStdGetAuxUserType(&clsid, AUXUSERTYPE_SHORTNAME,
								   (LPSTR)szLabel, OLEUI_CCHLABELMAX, NULL)) {

		 if ('\0'==szDocument[0]) {
			 LoadString(
				 s_hInst,IDS_DEFICONLABEL,szDocument,sizeof(szDocument));
		 }
		 lstrcpy(szLabel, szDocument);
	  }
  }


  hMetaPict = OleUIMetafilePictFromIconAndLabel(hDefIcon,
												szLabel,
												(LPSTR)szIconFile,
												IconIndex);

  DestroyIcon(hDefIcon);

  return hMetaPict;

}



/*
 * GetIconOfClass(HINSTANCE hInst, REFCLSID rclsid, LPSTR lpszLabel, BOOL fUseTypeAsLabel)
 *
 * Purpose:
 *  Returns a hMetaPict containing an icon and label (human-readable form
 *  of class) for the specified clsid.
 *
 * Parameters:
 *  hinst
 *  rclsid          REFCLSID pointing to clsid to use.
 *  lpszLabel       label to use for icon.
 *  fUseTypeAsLabel Use the clsid's user type name as the icon's label.
 *
 * Return Value:
 *  HGLOBAL         hMetaPict containing the icon and label - if we
 *                  don't find the clsid in the reg db then we
 *                  return NULL.
 */

STDAPI_(HGLOBAL)    GetIconOfClass(HINSTANCE hInst, REFCLSID rclsid, LPSTR lpszLabel, BOOL fUseTypeAsLabel)
{

  char    szLabel[OLEUI_CCHLABELMAX];
  char    szIconFile[OLEUI_CCHPATHMAX];
  HICON   hDefIcon;
  UINT    IconIndex;
  HGLOBAL hMetaPict;


  s_hInst = hInst;

  if (!fUseTypeAsLabel)  // Use string passed in as label
  {
	if (NULL != lpszLabel)
	   LSTRCPYN(szLabel, lpszLabel, sizeof(szLabel));
	else
	   *szLabel = '\0';
  }
  else   // Use AuxUserType2 (short name) as label
  {

	  if (0 == OleStdGetAuxUserType(rclsid,
									AUXUSERTYPE_SHORTNAME,
									(LPSTR)szLabel,
									OLEUI_CCHLABELMAX,
									NULL))

	   // If we can't get the AuxUserType2, then try the long name
	   if (0 == OleStdGetUserTypeOfClass(rclsid, szLabel, OLEUI_CCHKEYMAX, NULL)) {
		 if ('\0'==szDocument[0]) {
			 LoadString(
				 s_hInst,IDS_DEFICONLABEL,szDocument,sizeof(szDocument));
		 }
		 lstrcpy(szLabel, szDocument);  // last resort
	   }
  }

  // Get the icon, icon index, and path to icon file
  hDefIcon = HIconAndSourceFromClass(rclsid,
				  (LPSTR)szIconFile,
				  &IconIndex);

  if (NULL == hDefIcon)  // Use Vanilla Document
  {
	lstrcpy((LPSTR)szIconFile, (LPSTR)szOLE2DLL);
	IconIndex = ICONINDEX;
	hDefIcon = ExtractIcon(s_hInst, szIconFile, IconIndex);
  }

  // Create the metafile
  hMetaPict = OleUIMetafilePictFromIconAndLabel(hDefIcon, szLabel,
												(LPSTR)szIconFile, IconIndex);

  DestroyIcon(hDefIcon);

  return hMetaPict;

}


/*
 * OleUIMetafilePictFromIconAndLabel
 *
 * Purpose:
 *  Creates a METAFILEPICT structure that container a metafile in which
 *  the icon and label are drawn.  A comment record is inserted between
 *  the icon and the label code so our special draw function can stop
 *  playing before the label.
 *
 * Parameters:
 *  hIcon           HICON to draw into the metafile
 *  pszLabel        LPSTR to the label string.
 *  pszSourceFile   LPSTR containing the local pathname of the icon
 *                  as we either get from the user or from the reg DB.
 *  iIcon           UINT providing the index into pszSourceFile where
 *                  the icon came from.
 *
 * Return Value:
 *  HGLOBAL         Global memory handle containing a METAFILEPICT where
 *                  the metafile uses the MM_ANISOTROPIC mapping mode.  The
 *                  extents reflect both icon and label.
 */

STDAPI_(HGLOBAL) OleUIMetafilePictFromIconAndLabel(HICON hIcon, LPSTR pszLabel
	, LPSTR pszSourceFile, UINT iIcon)
	{
	HDC             hDC, hDCScreen;
	HMETAFILE       hMF;
	HGLOBAL         hMem;
	LPMETAFILEPICT  pMF;
	UINT            cxIcon, cyIcon;
	UINT            cxText, cyText;
	UINT            cx, cy;
	UINT            cchLabel = 0;
	HFONT           hFont, hFontT;
	int             cyFont;
	char            szIndex[10];
	RECT            TextRect;
	SIZE            size;
	POINT           point;
	UINT            fuAlign;

	if (NULL==hIcon)  // null label is valid but NOT a null icon
		return NULL;

	//Create a memory metafile
	hDC=(HDC)CreateMetaFile(NULL);

	if (NULL==hDC)
		return NULL;

	//Allocate the metafilepict
	hMem=GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, sizeof(METAFILEPICT));

	if (NULL==hMem)
		{
		hMF=CloseMetaFile(hDC);
		DeleteMetaFile(hMF);
		return NULL;
		}


	if (NULL!=pszLabel)
		{
		cchLabel=lstrlen(pszLabel);

		if (cchLabel >= OLEUI_CCHLABELMAX)
		   pszLabel[cchLabel] = '\0';   // truncate string
		}

	//Need to use the screen DC for these operations
	hDCScreen=GetDC(NULL);
	cyFont=-(8*GetDeviceCaps(hDCScreen, LOGPIXELSY))/72;

	//cyFont was calculated to give us 8 point.
	hFont=CreateFont(cyFont, 5, 0, 0, FW_NORMAL, 0, 0, 0, ANSI_CHARSET
		, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY
		, FF_SWISS, "MS Sans Serif");

	hFontT=SelectObject(hDCScreen, hFont);

	GetTextExtentPoint(hDCScreen,szMaxWidth,lstrlen(szMaxWidth),&size);
	SelectObject(hDCScreen, hFontT);

	cxText = size.cx;
	cyText = size.cy * 2;

	cxIcon = GetSystemMetrics(SM_CXICON);
	cyIcon = GetSystemMetrics(SM_CYICON);


	// If we have no label, then we want the metafile to be the width of
	// the icon (plus margin), not the width of the fattest string.
	if ( (NULL == pszLabel) || ('\0' == *pszLabel) )
		cx = cxIcon + cxIcon / 4;
	else
		cx = max(cxText, cxIcon);

	cy=cyIcon+cyText+4;

	//Set the metafile size to fit the icon and label
	SetWindowOrgEx(hDC, 0, 0, &point);
	SetWindowExtEx(hDC, cx, cy, &size);

	//Set up rectangle to pass to OleStdIconLabelTextOut
	SetRectEmpty(&TextRect);

	TextRect.right = cx;
	TextRect.bottom = cy;

	//Draw the icon and the text, centered with respect to each other.
	DrawIcon(hDC, (cx-cxIcon)/2, 0, hIcon);

	//String that indicates where to stop if we're only doing icons
	Escape(hDC, MFCOMMENT, lstrlen(szIconOnly)+1, szIconOnly, NULL);

	SetTextColor(hDC, GetSysColor(COLOR_WINDOWTEXT));
	SetBkMode(hDC, TRANSPARENT);
	fuAlign = SetTextAlign(hDC, TA_LEFT | TA_TOP | TA_NOUPDATECP);

	OleStdIconLabelTextOut(hDC,
						   hFont,
						   0,
						   cy - cyText,
						   ETO_CLIPPED,
						   &TextRect,
						   pszLabel,
						   cchLabel,
						   NULL);

	//Write comments containing the icon source file and index.
	if (NULL!=pszSourceFile)
		{
		//+1 on string lengths insures the null terminator is embedded.
		Escape(hDC, MFCOMMENT, lstrlen(pszSourceFile)+1, pszSourceFile, NULL);

		cchLabel=wsprintf(szIndex, "%u", iIcon);
		Escape(hDC, MFCOMMENT, cchLabel+1, szIndex, NULL);
		}

	SetTextAlign(hDC, fuAlign);

	//All done with the metafile, now stuff it all into a METAFILEPICT.
	hMF=CloseMetaFile(hDC);

	if (NULL==hMF)
		{
		GlobalFree(hMem);
		ReleaseDC(NULL, hDCScreen);
		return NULL;
		}

	//Fill out the structure
	pMF=(LPMETAFILEPICT)GlobalLock(hMem);

	//Transform to HIMETRICS
	cx=XformWidthInPixelsToHimetric(hDCScreen, cx);
	cy=XformHeightInPixelsToHimetric(hDCScreen, cy);
	ReleaseDC(NULL, hDCScreen);

	pMF->mm=MM_ANISOTROPIC;
	pMF->xExt=cx;
	pMF->yExt=cy;
	pMF->hMF=hMF;

	GlobalUnlock(hMem);

	DeleteObject(hFont);

	return hMem;
	}

#endif  // OBSOLETE


/*
 * GetAssociatedExecutable
 *
 * Purpose:  Finds the executable associated with the provided extension
 *
 * Parameters:
 *   lpszExtension   LPSTR points to the extension we're trying to find
 *                   an exe for. Does **NO** validation.
 *
 *   lpszExecutable  LPSTR points to where the exe name will be returned.
 *                   No validation here either - pass in 128 char buffer.
 *
 * Return:
 *   BOOL            TRUE if we found an exe, FALSE if we didn't.
 *
 */

BOOL FAR PASCAL GetAssociatedExecutable(LPSTR lpszExtension, LPSTR lpszExecutable)

{
   HKEY    hKey;
   LONG    dw;
   LRESULT lRet;
   char    szValue[OLEUI_CCHKEYMAX];
   char    szKey[OLEUI_CCHKEYMAX];
   LPSTR   lpszTemp, lpszExe;


   lRet = RegOpenKey(HKEY_CLASSES_ROOT, NULL, &hKey);

   if (ERROR_SUCCESS != lRet)
	  return FALSE;

   dw = OLEUI_CCHPATHMAX;
   lRet = RegQueryValue(hKey, lpszExtension, (LPSTR)szValue, &dw);  //ProgId

   if (ERROR_SUCCESS != lRet)
   {
	  RegCloseKey(hKey);
	  return FALSE;
   }


   // szValue now has ProgID
   lstrcpy(szKey, szValue);
   lstrcat(szKey, "\\Shell\\Open\\Command");


   dw = OLEUI_CCHPATHMAX;
   lRet = RegQueryValue(hKey, (LPSTR)szKey, (LPSTR)szValue, &dw);

   if (ERROR_SUCCESS != lRet)
   {
	  RegCloseKey(hKey);
	  return FALSE;
   }

   // szValue now has an executable name in it.  Let's null-terminate
   // at the first post-executable space (so we don't have cmd line
   // args.

   lpszTemp = (LPSTR)szValue;

   while (('\0' != *lpszTemp) && (isspace(*lpszTemp)))
	  lpszTemp++;     // Strip off leading spaces

   lpszExe = lpszTemp;

   while (('\0' != *lpszTemp) && (!isspace(*lpszTemp)))
	  lpszTemp++;     // Step through exe name

   *lpszTemp = '\0';  // null terminate at first space (or at end).


   lstrcpy(lpszExecutable, lpszExe);

   return TRUE;

}


/*
 * HIconAndSourceFromClass
 *
 * Purpose:
 *  Given an object class name, finds an associated executable in the
 *  registration database and extracts the first icon from that
 *  executable.  If none is available or the class has no associated
 *  executable, this function returns NULL.
 *
 * Parameters:
 *  rclsid          pointer to clsid to look up.
 *  pszSource       LPSTR in which to place the source of the icon.
 *                  This is assumed to be OLEUI_CCHPATHMAX
 *  puIcon          UINT FAR * in which to store the index of the
 *                  icon in pszSource.
 *
 * Return Value:
 *  HICON           Handle to the extracted icon if there is a module
 *                  associated to pszClass.  NULL on failure to either
 *                  find the executable or extract and icon.
 */

HICON WINAPI HIconAndSourceFromClass(REFCLSID rclsid, LPSTR pszSource, UINT FAR *puIcon)
	{
	HICON           hIcon;
	UINT            IconIndex;

	if (NULL==rclsid || NULL==pszSource || IsEqualCLSID(rclsid,&CLSID_NULL))
		return NULL;

	if (!FIconFileFromClass(rclsid, pszSource, OLEUI_CCHPATHMAX, &IconIndex))
		return NULL;

	hIcon=ExtractIcon(ghInst, pszSource, IconIndex);

	if ((HICON)32 > hIcon)
		hIcon=NULL;
	else
		*puIcon= IconIndex;

	return hIcon;
	}


/*
 * FIconFileFromClass
 *
 * Purpose:
 *  Looks up the path to executable that contains the class default icon.
 *
 * Parameters:
 *  rclsid          pointer to CLSID to look up.
 *  pszEXE          LPSTR at which to store the server name
 *  cch             UINT size of pszEXE
 *  lpIndex         LPUINT to index of icon within executable
 *
 * Return Value:
 *  BOOL            TRUE if one or more characters were loaded into pszEXE.
 *                  FALSE otherwise.
 */

BOOL WINAPI FIconFileFromClass(REFCLSID rclsid, LPSTR pszEXE, UINT cch, UINT FAR *lpIndex)
{

	LONG          dw;
	LONG          lRet;
	HKEY          hKey;
	LPMALLOC      lpIMalloc;
	HRESULT       hrErr;
	LPSTR         lpBuffer;
	LPSTR         lpIndexString;
	UINT          cBufferSize = 136;// room for 128 char path and icon's index
	char          szKey[64];
	LPSTR         pszClass;


	if (NULL==rclsid || NULL==pszEXE || 0==cch || IsEqualCLSID(rclsid,&CLSID_NULL))
		return FALSE;

	//Here, we use CoGetMalloc and alloc a buffer (maxpathlen + 8) to
	//pass to RegQueryValue.  Then, we copy the exe to pszEXE and the
	//index to *lpIndex.

	hrErr = CoGetMalloc(MEMCTX_TASK, &lpIMalloc);

	if (NOERROR != hrErr)
	  return FALSE;

	lpBuffer = (LPSTR)lpIMalloc->lpVtbl->Alloc(lpIMalloc, cBufferSize);

	if (NULL == lpBuffer)
	{
	  lpIMalloc->lpVtbl->Release(lpIMalloc);
	  return FALSE;
	}


	if (CoIsOle1Class(rclsid))
	{

	  LPSTR lpszProgID;

	  // we've got an ole 1.0 class on our hands, so we look at
	  // progID\protocol\stdfileedting\server to get the
	  // name of the executable.

	  ProgIDFromCLSID(rclsid, &lpszProgID);

	  //Open up the class key
	  lRet=RegOpenKey(HKEY_CLASSES_ROOT, lpszProgID, &hKey);

	  if (ERROR_SUCCESS != lRet)
	  {
		 lpIMalloc->lpVtbl->Free(lpIMalloc, lpszProgID);
		 lpIMalloc->lpVtbl->Free(lpIMalloc, lpBuffer);
		 lpIMalloc->lpVtbl->Release(lpIMalloc);
		 return FALSE;
	  }

	  dw=(LONG)cBufferSize;
	  lRet = RegQueryValue(hKey, "Protocol\\StdFileEditing\\Server", lpBuffer, &dw);

	  if (ERROR_SUCCESS != lRet)
	  {

		 RegCloseKey(hKey);
		 lpIMalloc->lpVtbl->Free(lpIMalloc, lpszProgID);
		 lpIMalloc->lpVtbl->Free(lpIMalloc, lpBuffer);
		 lpIMalloc->lpVtbl->Release(lpIMalloc);
		 return FALSE;
	  }


	  // Use server and 0 as the icon index
	  LSTRCPYN(pszEXE, lpBuffer, cch);

	  *lpIndex = 0;

	  RegCloseKey(hKey);
	  lpIMalloc->lpVtbl->Free(lpIMalloc, lpszProgID);
	  lpIMalloc->lpVtbl->Free(lpIMalloc, lpBuffer);
	  lpIMalloc->lpVtbl->Release(lpIMalloc);
	  return TRUE;

	}



	/*
	 * We have to go walking in the registration database under the
	 * classname, so we first open the classname key and then check
	 * under "\\DefaultIcon" to get the file that contains the icon.
	 */

	StringFromCLSID(rclsid, &pszClass);

	lstrcpy(szKey, "CLSID\\");
	lstrcat(szKey, pszClass);

	//Open up the class key
	lRet=RegOpenKey(HKEY_CLASSES_ROOT, szKey, &hKey);

	if (ERROR_SUCCESS != lRet)
	{
		lpIMalloc->lpVtbl->Free(lpIMalloc, lpBuffer);
		lpIMalloc->lpVtbl->Free(lpIMalloc, pszClass);
		lpIMalloc->lpVtbl->Release(lpIMalloc);
		return FALSE;
	}

	//Get the executable path and icon index.

	dw=(LONG)cBufferSize;
	lRet=RegQueryValue(hKey, "DefaultIcon", lpBuffer, &dw);

	if (ERROR_SUCCESS != lRet)
	{
	  // no DefaultIcon  key...try LocalServer

	  dw=(LONG)cBufferSize;
#ifdef WIN32
      lRet=RegQueryValue(hKey, "LocalServer32", lpBuffer, &dw);
#else
      lRet=RegQueryValue(hKey, "LocalServer", lpBuffer, &dw);
#endif

	  if (ERROR_SUCCESS != lRet)
	  {
		 // no LocalServer entry either...they're outta luck.

		 RegCloseKey(hKey);
		 lpIMalloc->lpVtbl->Free(lpIMalloc, lpBuffer);
		 lpIMalloc->lpVtbl->Free(lpIMalloc, pszClass);
		 lpIMalloc->lpVtbl->Release(lpIMalloc);
		 return FALSE;
	  }


	  // Use server from LocalServer or Server and 0 as the icon index
	  LSTRCPYN(pszEXE, lpBuffer, cch);

	  *lpIndex = 0;

	  RegCloseKey(hKey);
	  lpIMalloc->lpVtbl->Free(lpIMalloc, lpBuffer);
	  lpIMalloc->lpVtbl->Free(lpIMalloc, pszClass);
	  lpIMalloc->lpVtbl->Release(lpIMalloc);
	  return TRUE;
	}

	RegCloseKey(hKey);

	// lpBuffer contains a string that looks like "<pathtoexe>,<iconindex>",
	// so we need to separate the path and the icon index.

	lpIndexString = PointerToNthField(lpBuffer, 2, ',');

	if ('\0' == *lpIndexString)  // no icon index specified - use 0 as default.
	{
	   *lpIndex = 0;

	}
	else
	{
	   LPSTR lpTemp;
	   static char  szTemp[16];

	   lstrcpy((LPSTR)szTemp, lpIndexString);

	   // Put the icon index part into *pIconIndex
	   *lpIndex = atoi((const char *)szTemp);

	   // Null-terminate the exe part.
	   lpTemp = AnsiPrev(lpBuffer, lpIndexString);
	   *lpTemp = '\0';
	}

	if (!LSTRCPYN(pszEXE, lpBuffer, cch))
	{
	   lpIMalloc->lpVtbl->Free(lpIMalloc, lpBuffer);
	   lpIMalloc->lpVtbl->Free(lpIMalloc, pszClass);
	   lpIMalloc->lpVtbl->Release(lpIMalloc);
	   return FALSE;
	}

	// Free the memory we alloc'd and leave.
	lpIMalloc->lpVtbl->Free(lpIMalloc, lpBuffer);
	lpIMalloc->lpVtbl->Free(lpIMalloc, pszClass);
	lpIMalloc->lpVtbl->Release(lpIMalloc);
	return TRUE;
}



/*
 * OleStdIconLabelTextOut
 *
 * Purpose:
 *  Replacement for DrawText to be used in the "Display as Icon" metafile.
 *  Uses ExtTextOut to output a string center on (at most) two lines.
 *  Uses a very simple word wrap algorithm to split the lines.
 *
 * Parameters:  (same as for ExtTextOut, except for hFont)
 *  hDC           device context to draw into; if this is NULL, then we don't
 *                ETO the text, we just return the index of the beginning
 *                of the second line
 *  hFont         font to use
 *  nXStart       x-coordinate of starting position
 *  nYStart       y-coordinate of starting position
 *  fuOptions     rectangle type
 *  lpRect        rect far * containing rectangle to draw text in.
 *  lpszString    string to draw
 *  cchString     length of string (truncated if over OLEUI_CCHLABELMAX)
 *  lpDX          spacing between character cells
 *
 * Return Value:
 *  UINT          Index of beginning of last line (0 if there's only one
 *                line of text).
 *
 */

STDAPI_(UINT) OleStdIconLabelTextOut(HDC        hDC,
									 HFONT      hFont,
									 int        nXStart,
									 int        nYStart,
									 UINT       fuOptions,
									 RECT FAR * lpRect,
									 LPSTR      lpszString,
									 UINT       cchString,
									 int FAR *  lpDX)
{

  HDC          hDCScreen;
  static char  szTempBuff[OLEUI_CCHLABELMAX];
  int          cxString, cyString, cxMaxString;
  int          cxFirstLine, cyFirstLine, cxSecondLine;
  int          index;
  int          cch = cchString;
  char         chKeep;
  LPSTR        lpszSecondLine;
  HFONT        hFontT;
  BOOL         fPrintText = TRUE;
  UINT         iLastLineStart = 0;
  SIZE         size;

  // Initialization stuff...

  if (NULL == hDC)  // If we got NULL as the hDC, then we don't actually call ETO
	fPrintText = FALSE;


  // Make a copy of the string (NULL or non-NULL) that we're using
  if (NULL == lpszString)
	*szTempBuff = '\0';

  else
	LSTRCPYN(szTempBuff, lpszString, sizeof(szTempBuff));

  // set maximum width
  cxMaxString = lpRect->right - lpRect->left;

  // get screen DC to do text size calculations
  hDCScreen = GetDC(NULL);

  hFontT=SelectObject(hDCScreen, hFont);

  // get the extent of our label
  GetTextExtentPoint(hDCScreen, szTempBuff, cch, &size);

  cxString = size.cx;
  cyString = size.cy;

  // Select in the font we want to use
  if (fPrintText)
	 SelectObject(hDC, hFont);

  // String is smaller than max string - just center, ETO, and return.
  if (cxString <= cxMaxString)
  {

	if (fPrintText)
	   ExtTextOut(hDC,
				  nXStart + (lpRect->right - cxString) / 2,
				  nYStart,
				  fuOptions,
				  lpRect,
				  szTempBuff,
				  cch,
				  NULL);

	iLastLineStart = 0;  // only 1 line of text
	goto CleanupAndLeave;
  }

  // String is too long...we've got to word-wrap it.


  // Are there any spaces, slashes, tabs, or bangs in string?

  if (lstrlen(szTempBuff) != (int)strcspn(szTempBuff, szSeparators))
  {
	 // Yep, we've got spaces, so we'll try to find the largest
	 // space-terminated string that will fit on the first line.

	 index = cch;


	 while (index >= 0)
	 {

	   char cchKeep;

	   // scan the string backwards for spaces, slashes, tabs, or bangs

	   while (!IS_SEPARATOR(szTempBuff[index]) )
		 index--;


	   if (index <= 0)
		 break;

	   cchKeep = szTempBuff[index];  // remember what char was there

	   szTempBuff[index] = '\0';  // just for now

	   GetTextExtentPoint(
			   hDCScreen, (LPSTR)szTempBuff,lstrlen((LPSTR)szTempBuff),&size);

	   cxFirstLine = size.cx;
	   cyFirstLine = size.cy;

	   szTempBuff[index] = cchKeep;   // put the right char back

	   if (cxFirstLine <= cxMaxString)
	   {

		   iLastLineStart = index + 1;

		   if (!fPrintText)
			 goto CleanupAndLeave;

		   ExtTextOut(hDC,
					  nXStart +  (lpRect->right - cxFirstLine) / 2,
					  nYStart,
					  fuOptions,
					  lpRect,
					  (LPSTR)szTempBuff,
					  index + 1,
					  lpDX);

		   lpszSecondLine = (LPSTR)szTempBuff;

		   lpszSecondLine += index + 1;

		   GetTextExtentPoint(hDCScreen,
									lpszSecondLine,
									lstrlen(lpszSecondLine),
									&size);

		   // If the second line is wider than the rectangle, we
		   // just want to clip the text.
		   cxSecondLine = min(size.cx, cxMaxString);

		   ExtTextOut(hDC,
					  nXStart + (lpRect->right - cxSecondLine) / 2,
					  nYStart + cyFirstLine,
					  fuOptions,
					  lpRect,
					  lpszSecondLine,
					  lstrlen(lpszSecondLine),
					  lpDX);

		   goto CleanupAndLeave;

	   }  // end if

	   index--;

	 }  // end while

  }  // end if

  // Here, there are either no spaces in the string (strchr(szTempBuff, ' ')
  // returned NULL), or there spaces in the string, but they are
  // positioned so that the first space terminated string is still
  // longer than one line. So, we walk backwards from the end of the
  // string until we find the largest string that will fit on the first
  // line , and then we just clip the second line.

  cch = lstrlen((LPSTR)szTempBuff);

  chKeep = szTempBuff[cch];
  szTempBuff[cch] = '\0';

  GetTextExtentPoint(hDCScreen, szTempBuff, lstrlen(szTempBuff),&size);

  cxFirstLine = size.cx;
  cyFirstLine = size.cy;

  while (cxFirstLine > cxMaxString)
  {
	 // We allow 40 characters in the label, but the metafile is
	 // only as wide as 10 W's (for aesthetics - 20 W's wide looked
	 // dumb.  This means that if we split a long string in half (in
	 // terms of characters), then we could still be wider than the
	 // metafile.  So, if this is the case, we just step backwards
	 // from the halfway point until we get something that will fit.
	 // Since we just let ETO clip the second line

	 szTempBuff[cch--] = chKeep;
	 if (0 == cch)
	   goto CleanupAndLeave;

	 chKeep = szTempBuff[cch];
	 szTempBuff[cch] = '\0';

	 GetTextExtentPoint(
			 hDCScreen, szTempBuff, lstrlen(szTempBuff), &size);
	 cxFirstLine = size.cx;
  }

  iLastLineStart = cch;

  if (!fPrintText)
	goto CleanupAndLeave;

  ExtTextOut(hDC,
			 nXStart + (lpRect->right - cxFirstLine) / 2,
			 nYStart,
			 fuOptions,
			 lpRect,
			 (LPSTR)szTempBuff,
			 lstrlen((LPSTR)szTempBuff),
			 lpDX);

  szTempBuff[cch] = chKeep;
  lpszSecondLine = szTempBuff;
  lpszSecondLine += cch;

  GetTextExtentPoint(
		  hDCScreen, (LPSTR)lpszSecondLine, lstrlen(lpszSecondLine), &size);

  // If the second line is wider than the rectangle, we
  // just want to clip the text.
  cxSecondLine = min(size.cx, cxMaxString);

  ExtTextOut(hDC,
			 nXStart + (lpRect->right - cxSecondLine) / 2,
			 nYStart + cyFirstLine,
			 fuOptions,
			 lpRect,
			 lpszSecondLine,
			 lstrlen(lpszSecondLine),
			 lpDX);

CleanupAndLeave:
  SelectObject(hDCScreen, hFontT);
  ReleaseDC(NULL, hDCScreen);
  return iLastLineStart;

}



/*
 * PointerToNthField
 *
 * Purpose:
 *  Returns a pointer to the beginning of the nth field.
 *  Assumes null-terminated string.
 *
 * Parameters:
 *  lpszString        string to parse
 *  nField            field to return starting index of.
 *  chDelimiter       char that delimits fields
 *
 * Return Value:
 *  LPSTR             pointer to beginning of nField field.
 *                    NOTE: If the null terminator is found
 *                          Before we find the Nth field, then
 *                          we return a pointer to the null terminator -
 *                          calling app should be sure to check for
 *                          this case.
 *
 */
LPSTR WINAPI PointerToNthField(LPSTR lpszString, int nField, char chDelimiter)
{
   LPSTR lpField = lpszString;
   int   cFieldFound = 1;

   if (1 ==nField)
	  return lpszString;

   while (*lpField != '\0')
   {

	  if (*lpField++ == chDelimiter)
	  {

		 cFieldFound++;

		 if (nField == cFieldFound)
			return lpField;
	  }
   }

   return lpField;

}
