//*********************************************************************
//*                  Microsoft Windows                               **
//*            Copyright (c) 1994-1998 Microsoft Corporation
//*********************************************************************

//
//  UTIL.C - common utility functions
//

//  HISTORY:
//  
//  96/05/22  markdu  Created (from inetcfg.dll)
//

#include "pch.hpp"
#if 0
#include "string.h"
#endif

#define MAX_MSG_PARAM     8

// function prototypes
VOID _cdecl FormatErrorMessage(CHAR * pszMsg,DWORD cbMsg,CHAR * pszFmt,va_list ArgList);

/*******************************************************************

  NAME:    MsgBox

  SYNOPSIS:  Displays a message box with the specified string ID

********************************************************************/
int MsgBox(HWND hWnd,UINT nMsgID,UINT uIcon,UINT uButtons)
{
    CHAR szMsgBuf[MAX_RES_LEN+1];
  CHAR szSmallBuf[SMALL_BUF_LEN+1];

    LoadSz(IDS_APPNAME,szSmallBuf,sizeof(szSmallBuf));
    LoadSz(nMsgID,szMsgBuf,sizeof(szMsgBuf));

    MessageBeep(uIcon);
    return (MessageBox(hWnd,szMsgBuf,szSmallBuf,uIcon | uButtons));

}

/*******************************************************************

  NAME:    MsgBoxSz

  SYNOPSIS:  Displays a message box with the specified text

********************************************************************/
int MsgBoxSz(HWND hWnd,LPSTR szText,UINT uIcon,UINT uButtons)
{
  CHAR szSmallBuf[SMALL_BUF_LEN+1];
  LoadSz(IDS_APPNAME,szSmallBuf,sizeof(szSmallBuf));

    MessageBeep(uIcon);
    return (MessageBox(hWnd,szText,szSmallBuf,uIcon | uButtons));
}

/*******************************************************************

  NAME:    MsgBoxParam

  SYNOPSIS:  Displays a message box with the specified string ID

  NOTES:    extra parameters are string pointers inserted into nMsgID.

********************************************************************/
int _cdecl MsgBoxParam(HWND hWnd,UINT nMsgID,UINT uIcon,UINT uButtons,...)
{
  BUFFER Msg(3*MAX_RES_LEN+1);  // nice n' big for room for inserts
  BUFFER MsgFmt(MAX_RES_LEN+1);

  if (!Msg || !MsgFmt) {
    return MsgBox(hWnd,IDS_ERROutOfMemory,MB_ICONSTOP,MB_OK);
  }

    LoadSz(nMsgID,MsgFmt.QueryPtr(),MsgFmt.QuerySize());

  FormatErrorMessage(Msg.QueryPtr(),Msg.QuerySize(),
    MsgFmt.QueryPtr(),((CHAR *) &uButtons) + sizeof(uButtons));

  return MsgBoxSz(hWnd,Msg.QueryPtr(),uIcon,uButtons);
}


/*******************************************************************

  NAME:    LoadSz

  SYNOPSIS:  Loads specified string resource into buffer

  EXIT:    returns a pointer to the passed-in buffer

  NOTES:    If this function fails (most likely due to low
        memory), the returned buffer will have a leading NULL
        so it is generally safe to use this without checking for
        failure.

********************************************************************/
LPSTR LoadSz(UINT idString,LPSTR lpszBuf,UINT cbBuf)
{
  ASSERT(lpszBuf);

  // Clear the buffer and load the string
    if ( lpszBuf )
    {
        *lpszBuf = '\0';
        LoadString( ghInstance, idString, lpszBuf, cbBuf );
    }
    return lpszBuf;
}

/*******************************************************************

  NAME:    GetErrorDescription

  SYNOPSIS:  Retrieves the text description for a given error code
        and class of error (standard, setupx)

********************************************************************/
VOID GetErrorDescription(CHAR * pszErrorDesc,UINT cbErrorDesc,
  UINT uError,UINT uErrorClass)
{
  ASSERT(pszErrorDesc);

  // set a leading null in error description
  *pszErrorDesc = '\0';
  
  switch (uErrorClass) {

    case ERRCLS_STANDARD:

      if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,NULL,
        uError,0,pszErrorDesc,cbErrorDesc,NULL)) {
        // if getting system text fails, make a string a la
        // "error <n> occurred"
        CHAR szFmt[SMALL_BUF_LEN+1];
        LoadSz(IDS_ERRFORMAT,szFmt,sizeof(szFmt));
        wsprintf(pszErrorDesc,szFmt,uError);
      }

      break;

    case ERRCLS_SETUPX:

      GetSETUPXErrorText(uError,pszErrorDesc,cbErrorDesc);
      break;

    default:

      DEBUGTRAP("Unknown error class %lu in GetErrorDescription",
        uErrorClass);

  }

}
  
/*******************************************************************

  NAME:    FormatErrorMessage

  SYNOPSIS:  Builds an error message by calling FormatMessage

  NOTES:    Worker function for PrepareErrorMessage

********************************************************************/
VOID _cdecl FormatErrorMessage(CHAR * pszMsg,DWORD cbMsg,CHAR * pszFmt,va_list ArgList)
{
  ASSERT(pszMsg);
  ASSERT(pszFmt);

  // build the message into the pszMsg buffer
  DWORD dwCount = FormatMessage(FORMAT_MESSAGE_FROM_STRING,
    pszFmt,0,0,pszMsg,cbMsg,&ArgList);
  ASSERT(dwCount > 0);
}

/*******************************************************************

  NAME:    PrepareErrorMessage

  SYNOPSIS:  Displays an error message for given error 

  ENTRY:    hWnd - parent window
        uStrID - ID of string resource with message format.
          Should contain %1 to be replaced by error text,
          additional parameters can be specified as well.
        uError - error code for error to display
        uErrorClass - ERRCLS_xxx ID of class of error that
          uError belongs to (standard, setupx)
        uIcon - icon to display
        ... - additional parameters to be inserted in string
          specified by uStrID

********************************************************************/
VOID _cdecl PrepareErrorMessage(UINT uStrID,UINT uError,
  UINT uErrorClass,UINT uIcon,...)
{
  // dynamically allocate buffers for messages
  BUFFER ErrorDesc(MAX_RES_LEN+1);
  BUFFER ErrorFmt(MAX_RES_LEN+1);

  if (!ErrorDesc || !ErrorFmt)
  {
    return;
  }

  // get a text description based on the error code and the class
  // of error it is
  GetErrorDescription(ErrorDesc.QueryPtr(),
    ErrorDesc.QuerySize(),uError,uErrorClass);

  // load the string for the message format
  LoadSz(uStrID,ErrorFmt.QueryPtr(),ErrorFmt.QuerySize());

  LPSTR args[MAX_MSG_PARAM];
  args[0] = (LPSTR) ErrorDesc.QueryPtr();
  memcpy(&args[1],((CHAR *) &uIcon) + sizeof(uIcon),(MAX_MSG_PARAM - 1) * sizeof(LPSTR));

  FormatErrorMessage(gpszLastErrorText, MAX_ERROR_TEXT,
    ErrorFmt.QueryPtr(),(va_list) &args[0]);
}

/*******************************************************************

  NAME:    RunMlsetExe

  SYNOPSIS:  Runs mlset32.exe, an Exchange app that needs to be
        run after files are installed otherwise Exchange
        barfs

  NOTES:    We look in registry to find path to mlset32.exe

********************************************************************/
DWORD RunMlsetExe(HWND hwndOwner)
{
  DWORD dwRet = ERROR_SUCCESS;

  // get path to mlset32 out of registry
  RegEntry re(szRegPathSoftwareMicrosoft,HKEY_LOCAL_MACHINE);

  CHAR szAppPath[MAX_PATH+1];
  if (re.GetString(szRegValMlSet,szAppPath,sizeof(szAppPath))) {
    PROCESS_INFORMATION pi;
    STARTUPINFO sti;

    // set "SilentRunning" registry switch to make mlset32
    // not display the Exchange wizard
    RegEntry reSilent(szRegPathExchangeClientOpt,HKEY_LOCAL_MACHINE);
    reSilent.SetValue(szRegValSilentRunning,(DWORD) 1);

    ZeroMemory(&sti,sizeof(STARTUPINFO));
    sti.cb = sizeof(STARTUPINFO);
            
    // launch mlset32.exe
    BOOL fRet = CreateProcess(NULL, (LPSTR) szAppPath,
                           NULL, NULL, FALSE, 0, NULL, NULL,
                           &sti, &pi);
    if (fRet) {
      CloseHandle(pi.hThread);

      // wait for mlset to complete
      MsgWaitForMultipleObjectsLoop(pi.hProcess);

      CloseHandle(pi.hProcess);
    } else {
      dwRet = GetLastError();
    }

    // put our window in front of mlset32's
    SetForegroundWindow(hwndOwner);
  
  } else {
    dwRet = ERROR_FILE_NOT_FOUND;
  }

  return dwRet;
}

/*******************************************************************

  NAME:    RemoveRunOnceEntry

  SYNOPSIS:  Removes the specified value from setup runonce key

  ENTRY:    uResourceID - ID of value name in resource
          (may be localized)

********************************************************************/
VOID RemoveRunOnceEntry(UINT uResourceID)
{
  RegEntry re(szRegPathSetupRunOnce,HKEY_LOCAL_MACHINE);
  CHAR szValueName[SMALL_BUF_LEN+1];
  ASSERT(re.GetError() == ERROR_SUCCESS);
  re.DeleteValue(LoadSz(uResourceID,
    szValueName,sizeof(szValueName)));
}

/*******************************************************************

  NAME:    GenerateComputerNameIfNeeded

  SYNOPSIS:  Makes up and stores in the registry a computer and/or
        workgroup name if not already set.

  NOTES:    If we don't do this, user will get prompted for computer
        name and workgroup.  These aren't meaningful to the user
        so we'll just make something up if these aren't set.

********************************************************************/
BOOL GenerateComputerNameIfNeeded(VOID)
{
  CHAR szComputerName[CNLEN+1]="";
  CHAR szWorkgroupName[DNLEN+1]="";
  BOOL fNeedToSetComputerName = FALSE;

  // get the computer name out of the registry
  RegEntry reCompName(szRegPathComputerName,HKEY_LOCAL_MACHINE);
  if (reCompName.GetError() == ERROR_SUCCESS) {
    reCompName.GetString(szRegValComputerName,szComputerName,
      sizeof(szComputerName));
    if (!lstrlen(szComputerName)) {
      // no computer name set!  make one up
      GenerateDefaultName(szComputerName,sizeof(szComputerName),
        (CHAR *) szRegValOwner,IDS_DEF_COMPUTER_NAME);
      // store the generated computer name in the registry
      reCompName.SetValue(szRegValComputerName,szComputerName);

      // also need to store the computer name in the workgroup key
      // which we will open below... set a flag so we know to do this.
      // (don't ask me why they store the computer name in two places...
      // but we need to set both.)
      fNeedToSetComputerName = TRUE;
    }
  }

  // get the workgroup name out of the registry
  RegEntry reWorkgroup(szRegPathWorkgroup,HKEY_LOCAL_MACHINE);
  if (reWorkgroup.GetError() == ERROR_SUCCESS) {

    // if we set a new computer name up above, then we have to set
    // a 2nd copy of the new name now, in the workgroup key
    if (fNeedToSetComputerName) {
      reWorkgroup.SetValue(szRegValComputerName,szComputerName);
    }


    reWorkgroup.GetString(szRegValWorkgroup,szWorkgroupName,
      sizeof(szWorkgroupName));
    if (!lstrlen(szWorkgroupName)) {
      // no workgroup name set!  make one up
      GenerateDefaultName(szWorkgroupName,sizeof(szWorkgroupName),
        (CHAR *) szRegValOrganization,IDS_DEF_WORKGROUP_NAME);
      // store the generated workgroup name in the registry
      reWorkgroup.SetValue(szRegValWorkgroup,szWorkgroupName);
    }
  }

  return TRUE;
}

/*******************************************************************

  NAME:    GenerateDefaultName

  SYNOPSIS:  Generates default computer or workgroup name

  ENTRY:    pszName - buffer to be filled in with name
        cbName - size of cbName buffer
        pszRegValName - name of registry value in ...Windows\CurrentVersion
          key to generate name from
        uIDDefName - ID of string resource to use if no value is
          present in registry to generate name from

********************************************************************/
BOOL GenerateDefaultName(CHAR * pszName,DWORD cbName,CHAR * pszRegValName,
  UINT uIDDefName)
{
  ASSERT(pszName);
  ASSERT(pszRegValName);

  *pszName = '\0';  // NULL-terminate buffer

  // look for registered owner/organization name in registry
  RegEntry reSetup(szRegPathSetup,HKEY_LOCAL_MACHINE);
  if (reSetup.GetError() == ERROR_SUCCESS) {
    if (reSetup.GetString(pszRegValName,pszName,cbName) &&
      lstrlen(pszName)) {
      // got string from registry... now terminate at first whitespace
      CHAR * pch = pszName;
      while (*pch) {
        if (*pch == ' ') {
          // found a space, terminate here and stop
          *pch = '\0';           
        } else {
          // advance to next char, keep going
          pch = CharNext(pch);
        }
      }
      // all done!
      return TRUE; 
    }
  }
  
  // couldn't get this name from registry, go for our fallback name
  // from resource

  LoadSz(uIDDefName,pszName,cbName);
  return TRUE;
}

/*******************************************************************

  NAME:    MsgWaitForMultipleObjectsLoop

  SYNOPSIS:  Blocks until the specified object is signaled, while
        still dispatching messages to the main thread.

********************************************************************/
DWORD MsgWaitForMultipleObjectsLoop(HANDLE hEvent)
{
    MSG msg;
    DWORD dwObject;
    while (1)
    {
        // NB We need to let the run dialog become active so we have to half handle sent
        // messages but we don't want to handle any input events or we'll swallow the
        // type-ahead.
        dwObject = MsgWaitForMultipleObjects(1, &hEvent, FALSE,INFINITE, QS_ALLINPUT);
        // Are we done waiting?
        switch (dwObject) {
        case WAIT_OBJECT_0:
        case WAIT_FAILED:
            return dwObject;

        case WAIT_OBJECT_0 + 1:
      // got a message, dispatch it and wait again
      while (PeekMessage(&msg, NULL,0, 0, PM_REMOVE)) {
        DispatchMessage(&msg);
      }
            break;
        }
    }
    // never gets here
}


/*******************************************************************
// 10/24/96 jmazner Normandy 6968
// No longer neccessary thanks to Valdon's hooks for invoking ICW.


  NAME:    SetDesktopInternetIconToBrowser

  SYNOPSIS:  "Points" The Internet desktop icon to web browser
        (Internet Explorer)

  NOTES:    The Internet icon may initially "point" at this wizard,
        we need to set it to launch web browser once we complete
        successfully.

********************************************************************/
/********BOOL SetDesktopInternetIconToBrowser(VOID)
{
	CHAR szAppPath[MAX_PATH+1]="";
	BOOL fRet = FALSE;

	// look in the app path section in registry to get path to internet
	// explorer

	RegEntry reAppPath(szRegPathIexploreAppPath,HKEY_LOCAL_MACHINE);
	ASSERT(reAppPath.GetError() == ERROR_SUCCESS);
	if (reAppPath.GetError() == ERROR_SUCCESS) {

		reAppPath.GetString(szNull,szAppPath,sizeof(szAppPath));
		ASSERT(reAppPath.GetError() == ERROR_SUCCESS);

	}

	// set the path to internet explorer as the open command for the 
	// internet desktop icon
	if (lstrlen(szAppPath)) {
		RegEntry reIconOpenCmd(szRegPathInternetIconCommand,HKEY_CLASSES_ROOT);
		ASSERT(reIconOpenCmd.GetError() == ERROR_SUCCESS);
		if (reIconOpenCmd.GetError() == ERROR_SUCCESS) {
			UINT uErr = reIconOpenCmd.SetValue(szNull,szAppPath);
			ASSERT(uErr == ERROR_SUCCESS);
			
			fRet = (uErr == ERROR_SUCCESS);
		}
	}

	return fRet;
}
******/

/*******************************************************************

  NAME:    PrepareForRunOnceApp

  SYNOPSIS:  Copies wallpaper value in registry to make the runonce
        app happy

  NOTES:    The runonce app (the app that displays a list of apps
        that are run once at startup) has a bug.  At first boot,
        it wants to change the wallpaper from the setup wallpaper
        to what the user had before running setup.  Setup tucks
        the "old" wallpaper away in a private key, then changes
        the wallpaper to the setup wallpaper.  After the runonce
        app finishes, it looks in the private key to get the old
        wallpaper and sets that to be the current wallpaper.
        However, it does this all the time, not just at first boot!
        The end effect is that whenever you do anything that
        causes runonce.exe to run (add stuff thru add/remove
        programs control panel), your wallpaper gets set back to
        whatever it was when you installed win 95.  This is
        especially bad for Plus!, since wallpaper settings are an
        important part of the product.

        To work around this bug, we copy the current wallpaper settings
        (which we want preserved) to setup's private key.  When
        runonce runs it will say "aha!" and copy those values back
        to the current settings.

********************************************************************/
VOID PrepareForRunOnceApp(VOID)
{
  // open a key to the current wallpaper settings
  RegEntry reDesktop(szRegPathDesktop,HKEY_CURRENT_USER);
  ASSERT(reDesktop.GetError() == ERROR_SUCCESS);

  // open a key to the private setup section
  RegEntry reSetup(szRegPathSetupWallpaper,HKEY_LOCAL_MACHINE);
  ASSERT(reSetup.GetError() == ERROR_SUCCESS);

  if (reDesktop.GetError() == ERROR_SUCCESS &&
    reSetup.GetError() == ERROR_SUCCESS) {
    CHAR szWallpaper[MAX_PATH+1]="";
    CHAR szTiled[10]="";  // big enough for "1" + slop

    // get the current wallpaper name
    if (reDesktop.GetString(szRegValWallpaper,szWallpaper,
      sizeof(szWallpaper))) {

      // set the current wallpaper name in setup's private section
      UINT uRet=reSetup.SetValue(szRegValWallpaper,szWallpaper);
      ASSERT(uRet == ERROR_SUCCESS);

      // get the current 'tiled' value. 
      reDesktop.GetString(szRegValTileWallpaper,szTiled,
        sizeof(szTiled));

      // set the 'tiled' value in setup's section
      if (lstrlen(szTiled)) {
        uRet=reSetup.SetValue(szRegValTileWallpaper,szTiled);
        ASSERT(uRet == ERROR_SUCCESS);
      }
    }
  }
}
