windows-nt/Source/XPSP1/NT/admin/services/sched/schedui/sageset.cxx
2020-09-26 16:20:57 +08:00

763 lines
19 KiB
C++

//+----------------------------------------------------------------------------
//
// Scheduling Agent Service
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1996.
//
// File: sageset.cxx
//
// Contents: Support for Sage Settings
//
// History: 18-Jul-96 EricB created
// 3-06-1997 DavidMun added code to create sagerun argument
// 4-16-1997 DavidMun made it use strings from UI instead of
// from job object.
//
//-----------------------------------------------------------------------------
#include "..\pch\headers.hxx"
#pragma hdrstop
#include <mstask.h>
#include <job_cls.hxx>
#include <debug.hxx>
#include <sage.hxx>
#include "sageset.hxx"
#include "..\inc\misc.hxx"
#include "..\folderui\macros.h" // get ARRAYLEN macro
const CHAR SAGE_KEY[] = "SOFTWARE\\Microsoft\\Plus!\\System Agent\\SAGE";
const CHAR PROGRAM_VALUE[] ="Program";
const CHAR SETTINGS_VALUE[] = "Settings";
const CHAR SAGESET_PARAM[] = " /SAGESET:";
BOOL GetAppNameFromPath(CHAR * pszFullPathName, CHAR * pszAppName);
BOOL AppNamesMatch(CHAR * pszCommand1, CHAR * pszCommand2);
BOOL fAppCantSupportMultipleInstances(CHAR * pszCmd);
BOOL AnsiToIntA(LPCSTR pszString, int * piRet);
HRESULT GetNextSageRunParam(HKEY hkSageApp, PINT pnSetNum);
//+----------------------------------------------------------------------------
//
// Function: DoSageSettings
//
// Synopsis: Invoke the Sage-aware app to alter its schedule settings.
//
// Arguments: [szCommand] - app name portion of command line
// [szSageRun] - "" or contains /SAGERUN switch on input.
// contains /SAGERUN switch on output if app is
// sage-aware.
//
// Modifies: *[szSageRun]
//
// Returns: S_OK - application launched
// S_FALSE - application doesn't support sage settings
// E_*
//
// History: 3-06-1997 DavidMun Added [szNewArg] & [cchNewArg].
// 4-16-1997 DavidMun Take strings from UI instead of job
// object.
//
//-----------------------------------------------------------------------------
HRESULT
DoSageSettings(
LPSTR szCommand,
LPSTR szSageRun)
{
int nSetNum = 0;
if (!IsSageAware(szCommand, szSageRun, &nSetNum))
{
return S_FALSE;
}
//
// We are here because the user hit the "Settings..." button, so if the
// arguments don't include a sageset parameter, we need to add it.
//
if (!*szSageRun)
{
//
// IsSageAware set nSetNum to the next valid number to use.
//
wsprintf(szSageRun, "%s%u", SAGERUN_PARAM, nSetNum);
}
//
// Create a command line to invoke the sage-aware app with the sageset
// parameter.
//
CHAR szSetCommand[MAX_PATH];
CHAR szSetParams[MAX_PATH];
lstrcpyn(szSetCommand, szCommand, MAX_PATH);
lstrcpy(szSetParams, SAGESET_PARAM);
wsprintf(&szSetParams[lstrlen(szSetParams)], "%u", nSetNum);
if (!GetAppNameFromPath(szSetCommand, NULL)) //got a path?
{
//
// GetAppNameFromPath returns FALSE if szSetCommand is not a full
// path. So, see if the app has registered a path. If it has,
// GetAppPath places the full path name in szFullyQualified.
//
CHAR szFullyQualified[MAX_PATH];
GetAppPathInfo(szSetCommand,
szFullyQualified,
MAX_PATH,
NULL,
0);
if (*szFullyQualified)
{
lstrcpy(szSetCommand, szFullyQualified);
}
}
//
// Prefix the system path with the directories listed by the application
// in the app paths key.
//
BOOL fChangedPath;
LPSTR pszSavedPath;
fChangedPath = SetAppPath(szSetCommand, &pszSavedPath);
//
// Put the SageSet param onto the command line.
//
if (szSetParams[0] != ' ')
{
lstrcat(szSetCommand, " ");
}
lstrcat(szSetCommand, szSetParams);
DWORD dwErr = ERROR_SUCCESS;
STARTUPINFO sui;
PROCESS_INFORMATION pi;
ZeroMemory(&sui, sizeof(sui));
sui.cb = sizeof (STARTUPINFO);
if (CreateProcess(NULL,
szSetCommand,
NULL,
NULL,
FALSE,
CREATE_NEW_CONSOLE |
CREATE_NEW_PROCESS_GROUP,
NULL,
NULL,
&sui,
&pi))
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
else
{
dwErr = GetLastError();
ERR_OUT("DoSageSettings: CreateProcess", dwErr);
}
if (fChangedPath)
{
SetEnvironmentVariable(TEXT("PATH"), pszSavedPath);
delete [] pszSavedPath;
}
return (dwErr == ERROR_SUCCESS) ? S_OK : HRESULT_FROM_WIN32(dwErr);
}
//+--------------------------------------------------------------------------
//
// Function: IsSageAwareW
//
// Synopsis: Unicode wrapper for IsSageAware
//
// History: 10-27-1997 DavidMun Created
//
//---------------------------------------------------------------------------
BOOL
IsSageAwareW(WCHAR *pwzCmd, WCHAR *pwzParams, int *pnSetNum)
{
HRESULT hr;
CHAR szCmd[MAX_PATH+1];
CHAR szParam[MAX_PATH+1];
BOOL fResult = FALSE;
do
{
hr = UnicodeToAnsi(szCmd, pwzCmd, ARRAYLEN(szCmd));
BREAK_ON_FAIL(hr);
hr = UnicodeToAnsi(szParam, pwzParams, ARRAYLEN(szParam));
BREAK_ON_FAIL(hr);
fResult = IsSageAware(szCmd, szParam, pnSetNum);
} while (0);
return fResult;
}
//+----------------------------------------------------------------------------
//
// Function: IsSageAware
//
// Synopsis: Does this app use Sage settings?
//
// Arguments: [pszCmd] - The task application name property.
// [pszParams] - The task parameters, can be NULL if pnSetNum is
// NULL.
// [pnSetNum] - the registry setting set number to return, can
// be NULL if not needed.
//
// Returns: TRUE if it does, FALSE otherwise.
//
//-----------------------------------------------------------------------------
BOOL
IsSageAware(CHAR * pszCmd, const CHAR * pszParams, int * pnSetNum)
{
int index;
HKEY hSageKey;
HKEY hSageSubKey;
CHAR szSubKey[MAXPATH];
CHAR szSubKeyPath[MAXPATH];
CHAR szRegValue[MAXCOMMANDLINE];
long lRet;
CHAR *p;
BOOL fResult = FALSE;
DWORD cb;
if (pnSetNum != NULL)
{
*pnSetNum = 0;
}
if (RegOpenKey(HKEY_LOCAL_MACHINE, SAGE_KEY, &hSageKey))
{
return FALSE;
}
#define MAXSAGEPROGS 0xffffff
for (index = 0; index < MAXSAGEPROGS; index++)
{
//
// Examine each subkey of the Sage key.
//
lRet = RegEnumKey(hSageKey, index, szSubKey, MAXPATH);
if (lRet != ERROR_SUCCESS)
{
break;
}
lstrcpy(szSubKeyPath, SAGE_KEY);
lstrcat(szSubKeyPath, "\\");
lstrcat(szSubKeyPath, szSubKey);
if (RegOpenKey(HKEY_LOCAL_MACHINE,
szSubKeyPath,
&hSageSubKey) != ERROR_SUCCESS)
{
continue; //no path
}
//
// Look for a match on the application name.
//
cb = MAXPATH;
if (RegQueryValueEx(hSageSubKey,
PROGRAM_VALUE,
NULL,
NULL,
(LPBYTE)szRegValue,
&cb) != ERROR_SUCCESS)
{
RegCloseKey(hSageSubKey);
continue; //no path
}
schDebugOut((DEB_ITRACE,
"IsSageAware enum: pszCmd = %s, szRegValue = %s\n",
pszCmd, szRegValue));
RegCloseKey(hSageSubKey);
if (AppNamesMatch(pszCmd, szRegValue))
{
if (RegOpenKey(HKEY_LOCAL_MACHINE,
szSubKeyPath,
&hSageSubKey) != ERROR_SUCCESS)
{
continue; //no settings dialog
}
fResult = FALSE;
if (RegQueryValueEx(hSageSubKey,
SETTINGS_VALUE,
NULL,
NULL,
(LPBYTE)szRegValue,
&cb) == ERROR_SUCCESS)
{
if (szRegValue[0] == 0)
{
RegCloseKey(hSageSubKey);
break; // this means don't allow sage settings
}
}
else
{
RegCloseKey(hSageSubKey);
continue; //no settings registry key
}
// test for app that can't handle multiple instances
//
if (!fAppCantSupportMultipleInstances(pszCmd))
{
fResult = TRUE;
}
if (fResult && pnSetNum != NULL)
{
//
// Extract the set number from the parameters, if requested.
//
p = _tcsstr(pszParams, SAGERUN_PARAM);
if (p != NULL)
{
AnsiToIntA(p + lstrlen(SAGERUN_PARAM), pnSetNum);
}
else
{
//
// This job is for a sage-aware app which supports the
// SAGERUN switch, but it lacks that switch in its
// parameter property. Since the caller wants to know
// what value to use, generate one.
//
HRESULT hr = GetNextSageRunParam(hSageSubKey, pnSetNum);
if (FAILED(hr))
{
fResult = FALSE;
}
}
}
RegCloseKey(hSageSubKey);
break;
}
}
RegCloseKey(hSageKey);
return fResult;
}
const CHAR SET_PREFIX[] = "Set";
//+--------------------------------------------------------------------------
//
// Function: CreateSageRunKey
//
// Synopsis: Create a registry key with string representation of
// value [uiKey] for app [szSageAwareExe].
//
// Arguments: [szSageAwareExe] - app for which to create key
// [uiKey] - numeric representation of key name
//
// Returns: S_OK or E_FAIL
//
// History: 10-28-1997 DavidMun Created
//
//---------------------------------------------------------------------------
HRESULT
CreateSageRunKey(
LPCSTR szSageAwareExe,
UINT uiKey)
{
HRESULT hr = E_FAIL; // init for failure
int index;
HKEY hSageKey;
HKEY hSageSubKey;
CHAR szSubKey[MAXPATH];
CHAR szSubKeyPath[MAXPATH];
CHAR szRegValue[MAXCOMMANDLINE];
long lRet;
DWORD cb;
if (RegOpenKey(HKEY_LOCAL_MACHINE, SAGE_KEY, &hSageKey))
{
return hr;
}
for (index = 0; index < MAXSAGEPROGS; index++)
{
//
// Examine each subkey of the Sage key.
//
lRet = RegEnumKey(hSageKey, index, szSubKey, MAXPATH);
if (lRet != ERROR_SUCCESS)
{
break;
}
lstrcpy(szSubKeyPath, SAGE_KEY);
lstrcat(szSubKeyPath, "\\");
lstrcat(szSubKeyPath, szSubKey);
if (RegOpenKey(HKEY_LOCAL_MACHINE,
szSubKeyPath,
&hSageSubKey) != ERROR_SUCCESS)
{
continue; //no path
}
//
// Look for a match on the application name.
//
cb = MAXPATH;
if (RegQueryValueEx(hSageSubKey,
PROGRAM_VALUE,
NULL,
NULL,
(LPBYTE)szRegValue,
&cb) != ERROR_SUCCESS)
{
RegCloseKey(hSageSubKey);
continue; //no path
}
RegCloseKey(hSageSubKey);
if (AppNamesMatch((LPSTR)szSageAwareExe, szRegValue))
{
if (RegOpenKey(HKEY_LOCAL_MACHINE,
szSubKeyPath,
&hSageSubKey) != ERROR_SUCCESS)
{
continue; //no settings dialog
}
CHAR szKeyName[MAX_PATH];
HKEY hkNewKey = NULL;
wsprintf(szKeyName, "%s%u", SET_PREFIX, uiKey);
lRet = RegCreateKey(hSageSubKey, szKeyName, &hkNewKey);
if (hkNewKey)
{
RegCloseKey(hkNewKey);
}
RegCloseKey(hSageSubKey);
if (lRet == ERROR_SUCCESS)
{
hr = S_OK;
}
else
{
schDebugOut((DEB_ERROR,
"CreateSageRunKey: RegCreateKey %uL",
lRet));
}
break;
}
}
RegCloseKey(hSageKey);
return hr;
}
//+--------------------------------------------------------------------------
//
// Function: GetNextSageRunParam
//
// Synopsis: Fill *[pnSetNum] with the next <n> value to use for a new
// Set<n> subkey under [hSageAppSubKey].
//
// Arguments: [hSageAppSubKey] - handle to app's key under SAGE key
// [pnSetNum] - filled with next number to use
//
// Returns: HRESULT
//
// Modifies: *[pnSetNum]
//
// History: 3-06-1997 DavidMun Created
//
//---------------------------------------------------------------------------
HRESULT
GetNextSageRunParam(
HKEY hSageAppSubKey,
PINT pnSetNum)
{
HRESULT hr = S_OK;
ULONG idxKey;
*pnSetNum = 0;
for (idxKey = 0; TRUE; idxKey++)
{
LONG lr;
CHAR szSubKey[MAX_PATH + 1];
lr = RegEnumKey(hSageAppSubKey, idxKey, szSubKey, ARRAYLEN(szSubKey));
//
// Quit on error, including end of subkeys
//
if (lr != ERROR_SUCCESS)
{
if (lr != ERROR_NO_MORE_ITEMS)
{
hr = E_FAIL;
schDebugOut((DEB_ERROR,
"GetNextSageRunParam: RegEnumKey %uL",
lr));
}
break;
}
//
// Ignore this key if it doesn't start with "Set"
//
CHAR szSet[ARRAYLEN(SET_PREFIX)];
lstrcpyn(szSet, szSubKey, ARRAYLEN(SET_PREFIX));
if (lstrcmpi(szSet, SET_PREFIX))
{
continue;
}
//
// Get the numeric value after the prefix. If there's no valid
// number, ignore this key.
//
BOOL fNumber;
INT iSetN;
fNumber = AnsiToIntA(szSubKey + ARRAYLEN(SET_PREFIX) - 1, &iSetN);
if (!fNumber)
{
continue;
}
//
// If the number matches or exceeds the one we plan to use, make ours
// larger.
//
if (iSetN >= *pnSetNum)
{
*pnSetNum = iSetN + 1;
}
}
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: GetAppNameFromPath
//
// Synopsis:
//
// Arguments: [pszFullPathName] - full or partial path
// [pszAppName] - buffer for app name, may be NULL
//
// Returns: TRUE - [pszFullPathName] contained slashes
// FALSE - [pszFullPathName] didn't contain slashes
//
// Modifies: *[pszAppName]
//
// History: 10-25-96 DavidMun Made DBCS safe
//
//----------------------------------------------------------------------------
BOOL GetAppNameFromPath(CHAR * pszFullPathName, CHAR * pszAppName)
{
LPSTR pszLastSlash;
pszLastSlash = _tcsrchr(pszFullPathName, '\\');
if (!pszAppName)
{
return pszLastSlash != NULL;
}
if (pszLastSlash)
{
lstrcpy(pszAppName, pszLastSlash + 1);
}
else
{
lstrcpy(pszAppName, pszFullPathName);
}
schDebugOut((DEB_ITRACE, "GetAppNameFromPath app name: %s\n", pszAppName));
if (pszAppName[0] != '"')
{
LPSTR pszQuote = _tcschr(pszAppName, '"');
if (pszQuote)
{
*pszQuote = '\0';
}
}
return pszLastSlash != NULL;
}
BOOL AppNamesMatch(CHAR *pszCommand1, CHAR *pszCommand2)
{
CHAR short1[MAXPATH];
CHAR short2[MAXPATH];
GetAppNameFromPath(pszCommand1, short1);
GetAppNameFromPath(pszCommand2, short2);
if (lstrcmpi(short1, short2) == 0)
{
return(TRUE);
}
return(FALSE);
}
BOOL fAppCantSupportMultipleInstances(CHAR * pszCmd)
{
if (AppNamesMatch(pszCmd, "SCANDSKW.EXE") ||
AppNamesMatch(pszCmd, "DEFRAG.EXE") ||
AppNamesMatch(pszCmd, "CMPAGENT.EXE"))
{
if (FindWindow("ScanDskWDlgClass", NULL))
return TRUE;
if (FindWindow("MSDefragWClass1", NULL))
return TRUE;
if (FindWindow("MSExtraPakWClass1", NULL))
return TRUE;
}
return FALSE;
}
/*----------------------------------------------------------
Purpose: ScottH's version of atoi. Supports hexadecimal too.
If this function returns FALSE, *piRet is set to 0.
Returns: TRUE if the string is a number, or contains a partial number
FALSE if the string is not a number
*/
BOOL AnsiToIntA(LPCSTR pszString, int * piRet)
{
#define InRange(id, idFirst, idLast) \
((UINT)(id-idFirst) <= (UINT)(idLast-idFirst))
#define IS_DIGIT(ch) InRange(ch, '0', '9')
BOOL bRet;
int n;
BOOL bNeg = FALSE;
LPCSTR psz;
LPCSTR pszAdj;
// Skip leading whitespace
//
for (psz = pszString;
*psz == ' ' || *psz == '\n' || *psz == '\t';
psz = NextChar(psz))
;
// Determine possible explicit signage
//
if (*psz == '+' || *psz == '-')
{
bNeg = (*psz == '+') ? FALSE : TRUE;
psz++;
}
// Or is this hexadecimal?
//
pszAdj = NextChar(psz);
if (*psz == '0' && (*pszAdj == 'x' || *pszAdj == 'X'))
{
// Yes
// (Never allow negative sign with hexadecimal numbers)
bNeg = FALSE;
psz = NextChar(pszAdj);
pszAdj = psz;
// Do the conversion
//
for (n = 0; ; psz = NextChar(psz))
{
if (IS_DIGIT(*psz))
n = 0x10 * n + *psz - '0';
else
{
CHAR ch = *psz;
int n2;
if (ch >= 'a')
ch -= 'a' - 'A';
n2 = ch - 'A' + 0xA;
if (n2 >= 0xA && n2 <= 0xF)
n = 0x10 * n + n2;
else
break;
}
}
// Return TRUE if there was at least one digit
bRet = (psz != pszAdj);
}
else
{
// No
pszAdj = psz;
// Do the conversion
for (n = 0; IS_DIGIT(*psz); psz = NextChar(psz))
n = 10 * n + *psz - '0';
// Return TRUE if there was at least one digit
bRet = (psz != pszAdj);
}
*piRet = bNeg ? -n : n;
return bRet;
}