//+---------------------------------------------------------------------------- // // 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 #include #include #include #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 value to use for a new // Set 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; }